可以模拟我正在测试的类的部分吗?

时间:2013-10-17 09:25:34

标签: c# unit-testing moq

我目前正在为一个类编写单元测试,该类根据大型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本身。

2 个答案:

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

类型SomeStuffASomethingUserfullSomeOtherStuffSomeReallyUselessStuff是否已嵌套,无法测试或公开,是否可能?

如果可以测试类型SomeStuffASomethingUserfullSomeOtherStuffSomeReallyUselessStuff,那么最好将其实例注入到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读取器/解析器模拟作为参数创建其他类,并设置该模拟以期望来自它们的调用