我正在使用Moq和Nunit对C#代码进行单元测试,我想测试下面的简化方法。在单元测试方面,我认为我不应该检查foo值,因为FooClass可能会发生变化,并且随后会破坏我的测试。所以我能做的最少就是验证一切都被初始化并正确调用,也就是说,someContainer获取正确的值,而FooClass获取someContainer而不是其他一些MyContainer实例。我怎么做?这种(单位)测试方法是否正确?
public MyClass
{
...
public bool MyMethod(int a, int b, int c, out FooClass foo)
{
var someContainer = new MyContainer{
A = a,
B = b,
C = c
};
foo = new FooClass(someContainer,1,2,3);
...
return true;
}
...
}
答案 0 :(得分:2)
您无法测试foo
获取someContainer
,因为someContainer
只能从方法内部访问。对该方法的适当测试是foo
在正确的位置包含a
,b
和c
,并且它包含一些MyContainer
对象。它将是FooClass
构造函数的单元测试,它测试FooClass
对象包含正确的MyContainer
对象。
如果您的FooClass
会发生变化,那么您的MyMethod
也可能会发生变化,因此MyMethod
的任何测试都必须改变。它会破坏您的测试这一事实很好,因为这意味着您需要查看该方法并确保该方法能够满足您的需求,并且您正在测试它是否符合您的要求。你现在应该为现在的方法编写适当的测试,或者在FooClass
完成之前不要再编写任何测试。
答案 1 :(得分:1)
这种(单位)测试方法是否正确?
如果您想要模拟依赖项,以便可以测试代码的特定单元(例如示例中的MyClass
),那么您需要考虑将代码编写到需要注意的服务中不同的责任。在您的示例中,MyMethod
有几项责任:
MyContainer
FooClass
MyContainer
实例,FooClass
应使用。为了满足您测试MyMethod
将特定MyContainer
传递到FooClass
的愿望,您可以将责任1和2分成新的课程。通常,在C#中,您将为这些类提供一个接口,并使MyClass
将每个接口作为依赖项。 E.g。
public interface IFooClassProvider
{
FooClass CreateFooClass(MyContainer container, int d, int e, int f);
}
public interface IMyContainerProvider
{
MyContainer CreateMyContainer(int a, int b, int c);
}
public MyClass
{
private IMyContainerProvider _myContainerProvider;
private IFooClassProvider _fooClassProvider;
public MyClass(IMyContainerProvider myContainerProvider, IFooClassProvider fooClassProvider)
{
_myContainerProvider = myContainerProvider;
_fooClassProvider = fooClassProvider;
}
...
public bool MyMethod(int a, int b, int c, out FooClass foo)
{
var someContainer = _myContainerProvider.CreateMyContainer(a,b,c);
foo = _fooClassProvider.CreateFooClass(someContainer,1,2,3);
...
return true;
}
...
}
希望&#39;真实&#39;接口的实现是显而易见的。为了执行测试,您可以为每个接口创建模拟,例如new Mock<IFooclassProvider>()
并将其传递给MyClass
的构造函数。在单元测试中,您可以Setup
IMyContainerProvider
模拟提供您在单元测试中创建的特定容器实例。然后,您可以Verify
使用特定容器调用IFooClassProvider
模拟方法。
E.g。
var mockFooClassProvider = new Mock<IFooClassProvider>();
var mockMyContainerProvider = new Mock<MyContainerProvider>();
var myKnownContainer = new MyContainer(...);
mockMyContainerProvider.Setup(m => m.CreateMyContainer(It.IsAny<int>, ...))
.Returns(myKnownContainer);
var myClass = new MyClass(mockMyContainerProvider.Object, mockFooClassProvider.Object);
FooClass fooClass;
myClass.MyMethod(..., out fooClass);
mockFooClassProvider.Verify(m => m.CreateFooClass(myKnownContainer, It.IsAny<int>(),...);
对于这样一个简单的情况,这个答案正在把事情推向极端,但我认为问题中的例子是一些更复杂的代码的简化版本。以这种方式分离责任可以快速将代码简化为可测试的短片段。