单元测试业务逻辑接口

时间:2018-06-08 18:13:32

标签: c# nunit moq

我是以正确的方式来做这件事的吗?如果是这样,我能正确理解吗?我有点困惑。

我的项目设置在三个不同的层中。 UI层,业务层和数据访问层。业务层和数据访问层都是基于接口构建的。

我正在尝试使用NUnit和Moq编写单元测试。

这是我的例子。我想测试GetSum(int x,int y),这是一个只返回x + y的简单函数。此函数存在于CalculatorLogic中,并实现ICalculatorLogic。

public class CalculatorLogic : ICalculatorLogic
{
    public int GetSum(int x, int y)
    {
        return x + y;
    }
}

现在,我正在尝试编写UnitTest。

[TestFixture]
public class CalculatorLogicTests
{
    Mock<ICalculatorLogic> calculatorLogicMock;
    ICalculatorLogic calculatorLogic;

    public CalculatorLogicTests()
    {
        calculatorLogicMock = new Mock<ICalculatorLogic>();

        // now i need to do this setup, right?
        calculatorLogicMock.Setup(x => x.GetSum(It.IsAny<int>(), It.IsAny<int>())).Returns(6);

        calculatorLogic = calculatorLogicMock.Object;
    }

    [Test]
    public void GetSum_Test()
    {
        int expectedResult = 3 + 3;

        var sum = calculatorLogic.GetSum(3, 3);
        Assert.AreEqual(sum, expectedResult);
    }
}

现在,上述通过。它运行,它得到了我所期待的。但是,感觉不对。它只是返回我设置它在Setup()调用中返回的任何内容。如果我在Returns()而不是6中放了一个3,它就会失败。

我必须理解错误。否则,如果我告诉它要返回什么,我是否真的在测试我的功能?

1 个答案:

答案 0 :(得分:0)

好的,你有一个接口ICalculatorLogic和你希望为其编写测试的所谓接口CalculatorLogic的实现。

您正在使用模拟框架Moq来模拟CalculatorLogic没有的依赖项。你应该写这样的东西:

[TestFixture]
public class CalculatorLogicTests
{
    // Our unit under test.
    ICalculatorLogic calculatorLogic;

    [SetUp]
    public void SetUp()
    {
        // This method is called for every [Test] in this class.
        // So let's recreate our CalculatorLogic here so that each
        // test has a fresh instance.
        calculatorLogic = new CalculatorLogic();
    }

    [Test]
    public void GetSum_WithTwoIntegers_ReturnsTheirSum()
    {
        // Arrange
        int expectedResult = 3 + 3;

        // Act
        var sum = calculatorLogic.GetSum(3, 3);

        // Assert
        Assert.AreEqual(sum, expectedResult);
    }
}

现在让我们假设您希望GetSum记录调用它的参数。您可以像这样创建一个记录器界面:

public interface ILogger {
    void Log(int x, int y);
}

然后通过在CalculatorLogic类的构造函数中要求它将其作为依赖项:

public class CalculatorLogic : ICalculatorLogic
{
    private readonly ILogger logger;

    // Now we have a dependency on ILogger!
    public CalculatorLogic(ILogger l) {
        logger = l;
    }

    public int GetSum(int x, int y)
    {
        // Let's log those numbers!
        logger.Log(x, y);
        return x + y;
    }
} 

然后你可以这样编写你的测试(使用Moq):

[TestFixture]
public class CalculatorLogicTests
{
    // This guy we want to test.
    ICalculatorLogic calculatorLogic;

    // Our mock!
    Mock<ILogger> loggerMock;

    [SetUp]
    public void SetUp()
    {
        // Create the logger mock!
        loggerMock = new Mock<ILogger>();

        // Inject the logger into our CalculatorLogic!
        calculatorLogic = new CalculatorLogic(loggerMock.Object);
    }

    [Test]
    public void GetSum_WithTwoIntegers_ShouldCallLogger()
    {
        // Arrange
        int expectedResult = 3 + 3;

        // Act
        var sum = calculatorLogic.GetSum(3, 3);

        // Assert
        Assert.AreEqual(sum, expectedResult);

        // Verify that the logger's Log method was called once with x = 3 and y = 3.
        loggerMock.Verify(logger => logger.Log(It.Is<int>(x => x == 3), It.Is<int>(y => y == 3)), Times.Once());
    }
}