C# 模拟我正在测试的类的部分可以吗?
我目前正在为一个类编写单元测试,该类根据一个大xml文件中的参数格式化值 我正在测试的类在其构造函数中接收另一个类,该类提供解析和读取xml文件的功能。我认为给被测试类一个xml阅读类的具体实例是不好的,因为我相信这样做会导致每次我想测试主类的格式化函数时都要测试xml阅读类。如果xml读取类出现问题,格式化类中的所有单元测试都将失败,这显然不是格式化类的错误 那么,我应该如何继续 显然,我只需要创建一个xml读取类的模拟,并将其作为参数传递给构造函数。但是,格式化类将使用此实例创建其他类的大约5个私有实例 因为我不知道这些类想要做什么(老实说,这些测试不应该关心),所以我想模拟掉我正在测试的类的这些私有字段 这样做行吗?我该如何使用最小起订量 -编辑- 请参见以下示例:C# 模拟我正在测试的类的部分可以吗?,c#,unit-testing,moq,C#,Unit Testing,Moq,我目前正在为一个类编写单元测试,该类根据一个大xml文件中的参数格式化值 我正在测试的类在其构造函数中接收另一个类,该类提供解析和读取xml文件的功能。我认为给被测试类一个xml阅读类的具体实例是不好的,因为我相信这样做会导致每次我想测试主类的格式化函数时都要测试xml阅读类。如果xml读取类出现问题,格式化类中的所有单元测试都将失败,这显然不是格式化类的错误 那么,我应该如何继续 显然,我只需要创建一个xml读取类的模拟,并将其作为参数传递给构造函数。但是,格式化类将使用此实例创建其他类的大约
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
}
}
公共类FormatterCore:iFormatterFaceIwantToTest
{
公共格式化程序核心(IConfigService服务)
{
this.something=新事物a(服务);
this.somethingThatINeed=新的SomethingUserfull(服务);
this.somethingElse=新的SomeOtherStuff(服务);
this.SomeThingTotalyDifferent=新的SomeReallyUseLessTuff(服务);
//...
}
公共T格式(对象输入,字符串id)
{
//我要测试的接口的实现
}
}
在我的示例中,我想测试接口的方法
Format()
。要创建Formatter类的实例,我需要传递IConfigService实现的实例(这既昂贵又麻烦,因为它需要不同的xml文件,而且需要一段时间)。我这里的问题是,我不想为每个单元测试创建configService实例,因为这意味着我要用FormatterCore单元中的每个测试来测试configService本身 由于您无法访问XML格式化类的私有变量(除了通过反射侵入该类之外),并且无法确定其他类是何时创建的,因此我认为您无法以您希望的方式模拟它们。必须侵入一个类来访问私有变量或方法进行测试是一种代码味道——这意味着您拥有应该公开的隐藏的可测试功能
因此,要公开该功能,最好的做法似乎是注入工厂,XML格式化类使用这些工厂来创建这些其他类。您的XML读取器/解析器模拟将被传递给Create
方法,您将返回这些类的适当模拟供XML格式化类使用
或者,您可以像在集成测试中一样对待XML格式化类-接受将使用XML读取器/解析器模拟作为参数创建其他类,并设置该模拟以期望来自它们的调用。为了测试
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);
}
}
让您的IoC负责创建实例
每次测试都需要为所有依赖项创建和设置模拟。但是格式化类将使用此实例创建其他类的大约5个私有实例。很高兴看到这样一个例子。@DominicKexel我明白了,我编辑了我的初始问题,你能详细说明注射工厂的部分吗?如果我理解正确,这将需要对测试代码进行更改,以公开内部字段的创建。恐怕我做不到。啊,如果你不能更改
FormatterCore
,那么注入工厂就行不通了。我建议您将IConfigService
mock设置为期望来自SomeStuffA
和其他人的调用。想象一下FormatterCore
以及它作为单个单元创建的类。您可以使用反射将这些私有字段设置为其他mock(),但根据这些类之间的交互方式,确定这些类之间的依赖关系并设置存根调用可能非常棘手。
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
}
}