我目前正在为一个类编写单元测试,该类根据大型xml文件中的参数格式化值。
我正在测试的类在其构造函数中接收另一个类,该类提供解析和读取xml文件的功能。我认为给测试类一个xml读取类的具体实例是不好的方式,因为我相信这样做会导致每次我想要测试xml读取类 - 事实上 - 测试主类的格式化函数。如果xml读取类中存在问题,则格式化类中的所有单元测试都将失败,这显然不是格式化类的错误。
那我该怎么办?
显然,我只是创建一个xml读取类的模拟器,并将其作为参数传递给构造函数。但是,格式化类将使用此实例来创建其他类的大约5个私有实例。
因为我不知道这些类想做什么(老实说这些测试不应该关心)我想嘲笑我正在测试的类的这些私有字段。
这甚至可以吗?我怎么用Moq做到这一点?
-edit -
请参阅以下示例:
public class FormatterCore : IFormatterInterfaceIWantToTest
{
public FormatterCore(IConfigService service)
{
this.something = new SomeStuffA(service);
this.somethingThatINeed = new SomethingUserfull(service);
this.somethingElse = new SomeOtherStuff(service);
this.somethingTotallyDifferent = new SomeReallyUselessStuff(service);
//...
}
public T Format<T>(object input, string id)
{
// implementation of the interface I want to test
}
}
在我的示例中,我想测试接口的方法Format<T>()
。要创建Formatter类的实例,我需要传递一个IConfigService实现的实例(这是昂贵且繁琐的,因为它需要不同的xml文件并需要一段时间)。我的问题在于我不想为每个单元测试创建configService的实例,因为这意味着我将使用FormatterCore单元中的每个测试来测试configService本身。
答案 0 :(得分:1)
为了测试FormatterCore
,您不应该创建IConfigService
实现的实例。您必须创建并设置IConfigService
的模拟对象。
[TestClass]
public class FormatterCoreTest
{
Mock<IConfigService> сonfigServiceMock;
[TestInitialize]
public void Init()
{
сonfigServiceMock = new Mock<IConfigService>();
}
[TestMethod]
public void Format()
{
// arrange
var input = /* input value */;
var id = /* id value */;
var сonfigServiceMock
.Setup(services => services.YourMethodToMock())
.Returnes(/* expected result or behaviour */);
// act
var target = new FormatterCore(сonfigServiceMock.Object);
var result = target.Format</* AnyType */>(input, id);
// assert
/* Your asserts */
result.Should().Be(/* expectred result */);
Assert.AreEqual /* expectred result */, result);
}
}
类型SomeStuffA
,SomethingUserfull
,SomeOtherStuff
和SomeReallyUselessStuff
是否已嵌套,无法测试或公开,是否可能?
如果可以测试类型SomeStuffA
,SomethingUserfull
,SomeOtherStuff
和SomeReallyUselessStuff
,那么最好将其实例注入到FormatterCore的构造函数中,而不是在构造函数。
public class FormatterCore : IFormatterInterfaceIWantToTest
{
ISomeStuffA something;
ISomethingUserfull somethingThatINeed;
ISomeOtherStuff somethingElse;
ISomeReallyUselessStuff somethingTotallyDifferent;
public FormatterCore(
ISomeStuffA someStuffA,
ISomethingUserfull somethingUserfull,
ISomeOtherStuff someOtherStuff,
ISomeReallyUselessStuff someReallyUselessStuff
)
{
this.something = someStuffA;
this.somethingThatINeed = somethingUserfull;
this.somethingElse = someOtherStuff;
this.somethingTotallyDifferent = someReallyUselessStuff;
//...
}
public T Format<T>(object input, string id)
{
// implementation of the interface I want to test
}
}
让您的IoC负责实例创建。
需要在每次测试中为所有依赖项创建和设置模拟。
答案 1 :(得分:0)
由于您无法访问XML格式化类的私有变量(除了通过反射入侵类),并且您无法确定何时创建其他类,我认为您不能以你想要的方式嘲笑他们。不得不入侵一个类来访问私有变量或测试方法是一种代码味道 - 这意味着你已经隐藏了应该暴露的可测试功能。
因此,为了公开该功能,似乎最好的做法是注入XML格式化类用于创建这些其他类的工厂。您的XML阅读器/解析器模拟将传递给Create
方法,您将返回这些类的适当模拟以供XML格式化类使用。
或者,您可以像在集成测试中一样处理XML格式化类 - 接受将使用XML读取器/解析器模拟作为参数创建其他类,并设置该模拟以期望来自它们的调用