0

我目前正在为一个基于大 xml 文件中的参数格式化值的类编写单元测试。

我正在测试的类在其构造函数中接收另一个类,该类提供解析和读取 xml 文件的功能。我认为给被测试类一个 xml 读取类的具体实例是不好的风格,因为我相信这样做会导致每次我想测试 xml 读取类 - 事实上 - 测试主类的格式化功能. 如果xml读取类出现问题,格式化类中的所有单元测试都会失败,这显然不是格式化类的错。

那么我应该如何进行呢?

显然我会创建一个 xml 读取类的模拟并将其作为参数传递给构造函数。然而格式化类会使用这个实例来创建其他类的大约 5 个私有实例。

因为我不知道这些类想要做什么(老实说,这些测试不应该关心)我想模拟我正在测试的类的这些私有字段。

这样做可以吗?我将如何使用 Moq 做到这一点?

-编辑-

请参见以下示例:

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 本身。

4

2 回答 2

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);
    }
}

types SomeStuffA, SomethingUserfull, SomeOtherStuffand是SomeReallyUselessStuff嵌套的并且不能被测试或 public 并且有可能吗?

如果可以测试类型SomeStuffA,那么最好将它们的实例注入到 FormatterCore 的构造函数中,而不是在构造函数中创建它们SomethingUserfullSomeOtherStuffSomeReallyUselessStuff

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 负责创建实例。

在每个测试中都需要为所有依赖项创建和设置模拟。

于 2013-10-17T18:02:33.897 回答
0

由于您无法访问 XML 格式化类的私有变量(除了通过反射侵入该类),并且您无法确定其他类的创建时间,我认为您不能模拟它们你想要的方式。不得不侵入一个类来访问私有变量或测试方法是一种代码味道——这意味着你隐藏了应该公开的可测试功能。

因此,要公开该功能,您最好的做法似乎是注入 XML 格式化类用来创建这些其他类的工厂。您的 XML 阅读器/解析器模拟将被传递给Create方法,并且您将返回这些类的适当模拟以供 XML 格式化类使用。

或者,您可以像在集成测试中那样对待 XML 格式化类 - 接受将使用您的 XML 读取器/解析器模拟作为参数创建其他类,并设置该模拟以期望来自它们的调用。

于 2013-10-17T10:06:55.043 回答