单元测试断言最终结果或验证是否使用Moq调用参数

时间:2012-11-08 11:09:44

标签: unit-testing mocking moq stubbing

下面是我要测试的类(Class1),但我对单元测试并不完全满意。请参阅下面的代码示例。

受测试系统

public interface IRepository {
    string GetParameter(int id);
}

public class Repository {
    public string GetParameter(int id) {
        return "foo";
    }
}

public class ErrorInfo {
    public string ErrorCodes { get; set; }
}

public interface IErrorProvider {
    ErrorInfo BuildErrorMessage(string errorCodes);
}

public class ErrorProvider {
    public ErrorInfo BuildErrorMessage(string errorCodes) {
        return new ErrorInfo(){ErrorCodes = errorCodes};
    }
}

public class Class1 {
    private readonly IRepository _repository;
    private readonly IErrorProvider _errorProvider;
    public Class1(IRepository repository, IErrorProvider errorProvider) {
        _repository = repository;
        _errorProvider = errorProvider;
    }

    public List<ErrorInfo> GetErrorList(int id) {
        var errorList = new List<ErrorInfo>();
        string paramName = _repository.GetParameter(id);

        if (string.IsNullOrEmpty(paramName)) {
            string errorCodes = string.Format("{0}, {1}", 200, 201);
            var error = _errorProvider.BuildErrorMessage(errorCodes);
            errorList.Add(error);
        }

        return errorList;
    }
}

单元测试

在测试通过之下,我们检查被测系统中是否使用了正确的错误代码。

[TestMethod]
public void GetErrorList_WhenParameterIsEmpty_ReturnsExpectedErrorCodes2() {
        //Arrange
        var stubRepo = new Mock<IRepository>();
        stubRepo.Setup(x => x.GetParameter(It.IsAny<int>())).Returns(string.Empty);

        var stubErrorMock = new Mock<IErrorProvider>();

        const int id = 5;
        var sut = new Class1(stubRepo.Object, stubErrorMock.Object);

        //Act
        var result = sut.GetErrorList(id);

        //Verify
        string verifiableErrorCodes = "200, 201";
        stubErrorMock.Verify(x => x.BuildErrorMessage(verifiableErrorCodes));
}

但我更愿意测试最终结果。例如,我想要对生产代码中使用的错误代码进行断言。下面的测试失败但我想知道你如何对被测系统中使用的errorCodes进行断言的想法。

[TestMethod]
public void GetErrorList_WhenParameterIsEmpty_ReturnsExpectedErrorCodes1() {
        //Arrange
        var stubRepo = new Mock<IRepository>();
        stubRepo.Setup(x => x.GetParameter(It.IsAny<int>())).Returns(string.Empty);

        string expectedErrorCodes = "200, 201";
        var stubErrorRepo = new Mock<IErrorProvider>();
        stubErrorRepo.Setup(e => e.BuildErrorMessage(It.IsAny<string>()));

        const int id = 5;
        var sut = new Class1(stubRepo.Object, stubErrorRepo.Object);

        //Act
        var result = sut.GetErrorList(id);

        //Assert
        Assert.AreEqual(expectedErrorCodes, result.Single().ErrorCodes);
}

测试系统中使用的错误代码的正确方法是什么?

2 个答案:

答案 0 :(得分:1)

我建议只模拟IRepository并使用真实IErrorProvider。然后,您可以拨打GetErrorList(id)并查看结果。

答案 1 :(得分:0)

没有正确或错误的答案,我们决定使用Assert测试来测试最终结果。

我采用了TDD方法并重新实施/分析如下。

  • 从失败的测试开始(为了简化我从测试和sut中删除存储库的代码)

// AssertTest

    [TestMethod]
    public void GetErrorList_WhenParameterIsEmpty_ReturnsExpectedErrorCodes1()
    {
        //Arrange
        const string expectedErrorCodes = "200, 201";
        var stubErrorRepo = new Mock<IErrorProvider>();
        stubErrorRepo.Setup(e => e.BuildErrorMessage(expectedErrorCodes)).Returns(new ErrorInfo() { ErrorCodes = expectedErrorCodes });

        var sut = new Class1(stubErrorRepo.Object);

        //Act
        var result = sut.GetErrorList();

        //Assert
        Assert.AreEqual(expectedErrorCodes, result.Single().ErrorCodes);
    }

// SUT

    public IEnumerable<ErrorInfo> GetErrorList(int id)
    {           
        yield return new ErrorInfo();            
    }

正如您所期望的那样,测试失败了。

现在,如果编写足够的生产代码以使此测试通过。

    public IEnumerable<ErrorInfo> GetErrorList()
    {
        yield return _errorProvider.BuildErrorMessage("200, 201");
    }

对于上述SUT,VerifyTest仍然会失败。

// VerifyTest

   [TestMethod]
    public void GetErrorList_WhenParameterIsEmpty_ReturnsExpectedErrorCodes2()
    {
        //Arrange
        var stubErrorMock = new Mock<IErrorProvider>();
        var sut = new Class1(stubErrorMock.Object);

        //Act
        sut.GetErrorList();

        //Verify
        string verifiableErrorCodes = "200, 201";
        stubErrorMock.Verify(x => x.BuildErrorMessage(verifiableErrorCodes));
    }

但是如果我想要通过这个测试,我可以写下面的生产代码,如下所示

    public IEnumerable<ErrorInfo> GetErrorList()
    {
        _errorProvider.BuildErrorMessage("200, 201");
        return null;
    }

现在VerifyTest通过,但AssertTest失败。

这两项测试都以自己的方式有效。然而,他们测试不同的语义。 AssertTest测试最终结果是否包含正确的错误代码。验证测试确保 使用正确的错误代码调用该方法。重要的是要注意到这一点 Assert测试的结束值基于Moq提供的设置方法“匹配” 框架。换句话说,设置决定了最终结果。 如果设置配置不正确或生产代码使用的错误代码与设置配置不匹配,AssertTest将失败。

最好在测试最终结果时使用AssertTest。