我已经阅读了Ragzitsu关于同一问题的answer。我仍然对如何实现事情感到困惑。有人可以给我一个实现的例子。
我有以下课程:
class Fizz : IFizz
{
}
class Buzz : IBuzz
{
}
class Bar : IBar
{
}
class Foo : IFoo
{
public Foo(IBar bar, IFizz fizz, IBuzz buzz)
{
//initialize etc.
}
//public methods
}
在这里绕过构造函数的实用方法是什么?我想做像
这样的事情var foo = new Mock<IFoo>();
换句话说,代码如何看待建议
The best thing to do would be right click on your class and choose Extract interface.
答案 0 :(得分:23)
你可以通过引用MockBehavior来创建mock,其中构造函数有param参数,如下所示
Mock<testClass>(MockBehavior.Strict, new object[] {"Hello"});
答案 1 :(得分:8)
我知道这是在不久前被问到的,也许现在对我来说没有价值,但是对于未来的读者来说,让我们不要认为以下是同样的事情使用moq时:
var fooDouble = new Mock<IFoo>();
与:
不同var fooDouble = new Mock<Foo>();
我的理解是,在创建一个IFoo模型时,moq框架将生成一个实现IFoo接口的双类,从而在生成的实现中为自己提供默认构造函数。然而,Foo的模拟不直接在生成的代码中实现接口,而是从具体的Foo类继承,因此需要将任何要存根的方法设为虚拟,如果有任何具体的实现,则需要实际使用(例如,被测单位)然后你需要&#34; CallBase = true&#34;在你的模拟。如果您正在寻找具有构造函数参数的具体类,那么您可能正在寻找@Palkin的答案以避免暴露默认构造函数。
作为实际回答
问题的编辑代码如何看待建议最好的办法是......提取界面?
提取界面的建议可能不是您正在寻找的。正如在其他答案中提到的那样,我们不确定您实际上是在尝试测试。
如果您尝试在Foo
的具体 IFoo
实现上测试功能,那么提取界面并模拟IFoo
并不是真的要去为了帮助你,因为moq将实现界面本身并且仅提供&#34;功能&#34;你告诉它提供该实现的设置方法和属性。如果您正在进行此操作,那么 AND 正在测试的方法会在具体的Foo
实施中调用其他方法, AND 该方法是您要提供的方法这是一个抽象,然后我会像@Chris Marisic一样提到并改变你希望抽象到virtual
的方法。以虚拟为例,可以使用模拟框架来提供抽象,或者像过去那样做,并在测试类中测试受测试的主题。
public class MyFooTests
{
... // Tests
private class SubFoo : Foo
{
public override void MethodToAbstract(bool par)
{
// do something expected
}
}
}
使用这种方法你仍然需要为Foo的构造函数提供一些东西,但这将是在这些接口上使用模拟的主要情况。
现在,如果您正在测试Foo
并且测试中的方法只是调用IBar
,IFizz
,IBuzz
上的方法,并且您没有其他需要的抽象要创建,那么您已经设置好了。模拟这些接口,将它们设置为返回您在特定测试用例中的预期,并为Foo
提供模拟对象。
如@Chris Marisic所述,当被测对象依赖于Foo时,提取界面建议非常有用。但是,我可能不会因为不想在这种情况下创建接口而分享他的观点。这取决于你的气味:创建一个提供模拟能力的界面,或改变修饰符以提供相同的功能。如果您要更改修改器并仍然使用moq来模拟具体的Foo
,则需要公开默认构造函数(非常臭我)或创建使用给定的构造函数参数规范进行模拟,如referenced answer中所述。
假设您有以下内容:
public class Baz
{
private Foo foo;
public Baz(Foo inputFoo)
{
this.foo = inputFoo;
}
public bool GetFromTheFoo()
{
// other stuff here
var fooStuff = this.foo.GetStuff();
// logic on the stuff;
return fooStuff != null;
}
}
上述Baz
取决于Foo
。提取Foo
的界面后,Baz
需要接受IFoo
。
然后Foo
看起来就像你的问题一样IFoo
会为你想要从Baz
抽象出来的方法有一个签名定义。
object GetStuff();
现在,在您的测试中,您仍然会使用var foo = new Mock<IFoo>();
并将foo
提供给您的Baz
测试主题。不要忘记设置IFoo.GetStuff()
方法。
foo.Setup(f => f.GetStuff()).Returns(new object());
因为您现在已经创建了IFoo
抽象,所以不需要
在这里绕过构造函数?
因为IFoo
的模拟只有moq提供的默认构造函数。
我个人更喜欢使用界面方法,因为它会从IFizz
上的依赖关系链中删除IBuzz
,IBar
和Baz
。如果Baz
取决于Foo
而Foo
取决于IFizz etc.
,那么Baz
也取决于IFizz etc.
解压后IFoo
,{ {1}}仅取决于Baz
。
答案 2 :(得分:7)
如果您有一个接口IFoo
并且想要模拟具有参数的构造函数的Foo
类,则不应更改任何内容。
您的代码正是您所需要的。
var foo = new Mock<IFoo>();
以下建议涵盖了类没有接口且没有无参数构造函数的情况。实际上,您可以将所有需要的参数传递给Mock
的构造函数。
var mock = new Mock<IFoo>("constructor", "arguments");
但
“最好的办法是右击你的课程并选择Extract interface。” (c)中
答案 3 :(得分:7)
最好的办法是右键单击您的课程并选择Extract interface。
我将以正交的方式解决这个概念。我不同意这个陈述,界面不是解决问题的解决方案。
回到上一个问题的文字:
public class CustomerSyncEngine {
public CustomerSyncEngine(ILoggingProvider loggingProvider,
ICrmProvider crmProvider,
ICacheProvider cacheProvider) { ... }
public void MethodWithDependencies() {
loggingProvider.Log();
crmProvider.Crm();
cacheProvider.Cache();
}
}
请注意我添加的方法。
我认为真正的问题是,当您没有专门测试CustomerSyncEngine
时,而是在CustomerSyncEngine
上测试依赖的类。我们打电话给这个班级SuperSyncEngine
。创建针对SuperSyncEngine
的测试将会非常痛苦,因为您必须使用其3个接口模拟整个CustomerSyncEngine
,以及SuperSyncEngine
所具有的任何其他依赖项。
鉴于您要测试的代码SuperSyncEngine
取决于CustomerSyncEngine
,接口不是这里的答案。您可能创建ICustomerSyncEngine
但该界面不应仅为模拟框架创建。更好的解决方案是将CustomerSyncEngine.MethodWithDependencies
更改为虚拟
public virtual void MethodWithDependencies() {
loggingProvider.Log();
crmProvider.Crm();
cacheProvider.Cache();
}
这将允许您使用模拟框架替换该方法,忽略CustomerSyncEngine
附带的依赖项。
如果您遵循这种方法,您可能需要在CustomerSyncEngine
上公开默认构造函数以允许它被模拟。您可以解决这个问题,并使用null或其他值来满足依赖关系,但是当目标是减少摩擦时,这将是额外的工作。
答案 4 :(得分:5)
这是一个很老的帖子,但我遇到了类似的问题(从Moq开始)。如果其他人有类似的问题,这就是我所拥有的:
class Bar : IBar
{
}
class Foo : IFoo
{
public Foo(IBar bar)
{
//initialize etc.
}
//public methods
}
class Manager : IManager
{
public Manager(Foo foo)
{
//initialize etc
}
}
我要做的是测试Manager
而不是Foo
。
这是我的初始测试代码,它引发了错误。
[TestFixture]
public class ManagerTest
{
[Test]
public void SomeTest()
{
var fooMock = Mock<IFoo>();
var managerUnderTest = new Manager(fooMock.Object);
}
}
错误为Castle.DynamicProxy.InvalidProxyConstructorArgumentsException : Can not instantiate proxy of class: Something.Models.Foo. Could not find a parameterless constructor.
读取错误消息,Moq不了解如何实例化Foo,因为没有无参数构造函数,我们也没有告诉Moq如何使用参数实例化。将第二部分更改为:
[TestFixture]
public class ManagerTest
{
[Test]
public void SomeTest()
{
var barMock = Mock<IBar>();
var fooMock = Mock<IFoo>(barMock.Object);
var managerUnderTest = new Manager(fooMock.Object);
//proceed with test
}
}
答案 5 :(得分:3)
如果你去测试你的FOo课程,你不需要模拟。您只需要模拟那些依赖于您要测试的类的类。
类似的东西:
Mock<IBar> bar = new Mock<IBar>();
Mock<IBuzz> buzz = new Mock<IBuzz>();
Mock<IFizz> fizz= new Mock<IFizz>();
Foo foo = new Foo(bar.Object, buzz.Object, fizz.Object);
然后调用foo中要测试的方法;)
如果foo中的方法在bar / fuzz或fizz中使用了一些方法,那么你应该使用sintax:
buzz.Setup(x => x.DoSomething()).Returns(1);
这样当调用你的foo方法时,它会调用DoSomething并且总是会返回1;)
答案 6 :(得分:0)
当一种类型具有多个实现时,接口很有用。从消极方面来说,这意味着您应该注意不要仅仅因为您需要接口而导出接口(常见错误)。从积极的方面来说,模拟接口是定义的第二个实现。
因此,结论是 - 如果一个类型充当其他类型的依赖项,那么它是实现接口的一个很好的候选者。在这种情况下,您将能够自由而完整地模拟单元测试中的界面。
相关说明,在定义界面时,请确保仅向界面添加功能部件:定义所做的对象的方法,而不是的外观。拥有一堆getter / setter的接口不会增加设计的价值。这是一个相当大的理论领域,这个小窗口不是写更多关于它的地方。
澄清与您的问题的关联:模拟实现应该提供接口所需的行为。为此,您使用模拟框架的功能。这与Foo类的具体实现无关 - 您定义了Mock对象的特定行为。