假设我有以下设置
public interface IClass1
{
int Result(int val);
}
public interface IClass2
{
int Validate(int val);
}
然后我们有一个类实现其中一个接口,并将另一个作为构造函数中的参数。
public class Class1 : IClass1
{
private Class2 class2;
public Class1(IClass2 class2)
{
this.class2 = class2;
}
public int Result(int val)
{
return class2.Validate(val);
}
}
如果我然后为Class1的Result方法创建单元测试,我是否还应该通过注入Class2的实例来测试同一单元测试中Class2的Validate方法,还是应该单独测试?如果我也以同样的方式测试Validate方法,我是否进行了集成测试?
现在我创建一个Class2的存根,它返回Class2.Validate的预设值,然后在单元测试Class1.Return时检查是否已调用Validate方法。
我这样做是否正确?
答案 0 :(得分:1)
简短回答:不。
你应该一次测试一件事。
从Class1
的角度来看,class2
应该根据IClass2
工作,Class1
不应该“考虑它”。
想象一下,您将来某个时候替换IClass2
的实现 - 那么您是否希望更新与class1
相关的单元测试,因为class2
已更改?可能不是。
最好将责任分开,这是使用接口的原因的一部分:从Class1
的角度来看,你真的不知道Class2
做了什么,或者它是如何工作的 - 只是它应该实现IClass2
,这就足够了。
PS:使用测试工具,例如FakeItEasy,您可以发送IClass2
的伪实现,然后验证在调用Class1中的Result()
方法时实际调用了该实现。
换句话说,Class1
只是假设已经给出IClass2
的实现是值得信赖的 - 我们所需要做的就是确保我们实际上正在使用它。
答案 1 :(得分:0)
没有
在这种情况下的标准做法是将'mock'(在您的情况下为IClass2
类型)传递到构造函数中以测试Class1.Result
。您设计模拟以返回特定值,并测试Result
确实返回该值。
答案 2 :(得分:0)
查看隔离原则:http://agileinaflash.blogspot.co.uk/2012/04/is-your-unit-test-isolated.html和脏混合测试http://blog.stevensanderson.com/2009/08/24/writing-great-unit-tests-best-and-worst-practises/ 并研究一些隔离框架。我用Moq取得了很好的成功。
完全隔离的单元测试只能运行一种生产方法。如果你有两个类一起工作的实例,那就是集成测试。
在你的情况下:
public class Class1 : IClass1
{
private Class2 class2;
public Class1(IClass2 class2)
{
this.class2 = class2;
}
public int Result(int val)
{
return class2.Validate(val);
}
}
使用arrange act assert模式,Class1的单元测试之一将是:
[TestMethod]
public void Result_WithValidInput_CallsValidate
{
//used the MethodUnderTest_Condition_ExpectedResult naming convention
//Arrange
IClass2 mockClass2;
//TODO initialise mockClass2 with an a fake object using isolation framework, to return the relevant result to stop the code from falling over during test execution.
Class1 class1UnderTest = new Class1(mockClass2);
//Act
class1UnderTest.Result(1);
//Assert
//TODO use methods from isolation framework to assert that mockClass2.Validate() was called with the correct argument
}
Class1单元测试的行为证明它正确调用相关的IClass2方法,然后Class2的单元测试将证明它正常工作。
这测试了Class1如何与其成员交互,但这不是集成测试,因为它是针对IClass2而不是Class2的生产代码。调用的唯一生产代码在Class1中,并且作为单元与代码的其余部分完全隔离。
虽然严格来说,你不需要测试这个,因为Class2是私有的,它只是Class1.Result()的一个实现细节,你不需要测试如何 Result()做某事,只是它做到了。
您还可以考虑将IClass2设置为Result而不是Class1 ctor的参数,这样如果在所有方法中都不需要它,则不会传入它。如果您使用DI实例化所有依赖对象,这可以为您带来性能优势。