构造函数使用模拟对象,如何单独测试方法?
我有一个如下所示的类:
class MyClass {
private IDependency dep;
public MyClass(IDependency dep) {
this.dep = dep;
this.dep.Reset();
}
public void Clear() {
this.dep.Reset();
}
}
如何测试 Reset 方法是否在 Clear 方法中正确调用,同时忽略构造函数的作用?
我的 Moq 测试如下所示:
MockRepository mocks = new MockRepository(MockBehavior.Default);
var dep = mocks.Create<IDependency>();
dep.Setup(s => s.Reset());
MyClass myclass = new MyClass(dep.Object);
myclass.Clear():
state.Verify(s => s.Reset(), Times.Exactly(1));
它失败了,因为 Reset 已被调用两次(一次在构造函数中,一次在 Clear 方法中)。
I have a class that looks like this:
class MyClass {
private IDependency dep;
public MyClass(IDependency dep) {
this.dep = dep;
this.dep.Reset();
}
public void Clear() {
this.dep.Reset();
}
}
How do I test that the Reset method gets called properly in the Clear method while ignoring what the constructor does?
My Moq test looks like this:
MockRepository mocks = new MockRepository(MockBehavior.Default);
var dep = mocks.Create<IDependency>();
dep.Setup(s => s.Reset());
MyClass myclass = new MyClass(dep.Object);
myclass.Clear():
state.Verify(s => s.Reset(), Times.Exactly(1));
It fails because Reset has been called twice (once in the constructor, and once in the Clear method).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我希望有更好的方法来做到这一点,但是模拟将记录对
Reset
的所有调用,因此使用标准Verify
调用将始终返回 2。以下维护单独的柜台,不太优雅。如果有一种内置的方式可以用 Moq 来做到这一点,我很想知道。I hoped there would be a better way of doing it, but the mock will record all calls to
Reset
so using a standardVerify
call will always return 2. The following maintains a separate counter, which isn't very elegant. If there's a built-in way of doing this with Moq, I'd love to know.正如其他人所建议的,您可以推出自己的模拟,也可以对依赖项设置一些期望。
例如,您可以验证您的方法是否被调用:
但是值得指出的是,可以工作构造函数内有一种已知的代码气味,当您尝试编写测试时,这种气味会加剧。
事实上,您的构造函数需要在依赖项上调用此方法,这表明该对象知道有关依赖项的实现细节的太多信息。这违反了开闭原则,并且使您无法在初始化时调用 Reset 方法。
还要考虑到任何使用 MyClass 具体对象作为虚拟参数的类或测试都需要初始化 Mock,否则您将得到 NullReferenceException。这会增加编写测试的相当大的开销,并增加一定程度的脆弱性,相当于测试中的长期维护和漏报。解决这个问题的唯一方法是让一切都成为一个界面,虽然有效,但也不是最好的长期策略。
根据 http://googletesting.blogspot.com/2009/07/separation-anxiety .html,使用工厂会减少一些这种耦合,并使您能够更好地重用该对象。
As suggested by others, you can roll your own mock or you can set a number of expectations on the dependency.
For example, you can verify that your method was called:
However it's worth pointing out that work within the constructor is a known code smell, and this smell is exacerbated when you try to write tests.
The fact that your constructor needs to call this method on the dependency suggests that this object knows too much information about the implementation details of the dependency. This violates the Open Closed Principle and closes you off from scenarios where you don't want the Reset method from being called when it's initialized.
Also consider that any class or test that uses the MyClass concrete object as an dummy parameter will need a Mock initialized or you'll get a NullReferenceException. This adds considerable overhead to writing your tests and adds a level of fragility that equates to long term maintenance and false negatives in your tests. The only way around this is to make everything an interface which although effective isn't the best long term strategy either.
As per http://googletesting.blogspot.com/2009/07/separation-anxiety.html, the use of a Factory would reduce some of this coupling and open you up to better reuse of this object.
我遇到了同样的问题。
执行以下操作以使模拟仅记录 Clear 方法的行为:
I came across the same problem.
Do the following to have the mock only recording the behavior of the Clear method:
您可以编写一个间谍,而不是使用模拟对象。需要更多的编码,但测试更容易阅读。
测试可以写成
Instead of using a mock object, you could write a spy. Requires a bit more coding, but the test is easier to read.
The test could the be written as
您可以使用反射将私有字段 dep 设置为您的模拟对象。然后只需调用 Clear 方法并测试依赖项调用即可。
You can use reflection to set the private field dep to your mocked object. Then just call the Clear method and test the dependency call.