简体   繁体   English

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

[英]Unit Test Assert against end result or verifying whether the parameters were called using Moq

Below is a class (Class1) that I want to test, but I'm not fully satisfied with my Unit Test. 以下是我要测试的课程(Class1),但是我对单元测试不完全满意。 Please see below code samples. 请参见下面的代码示例。

System Under Test 被测系统

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

Unit Tests 单元测试

Below test passes and we check whether the correct error codes being used within the system under test. 在测试通过下方,我们检查被测系统内是否使用了正确的错误代码。

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

However I would prefer testing the end result. 但是我更喜欢测试最终结果。 For example, I want to Assert against the error codes that have been used in the production code. 例如,我要针对生产代码中使用的错误代码进行断言。 Below test fails but I like to know your thoughts on how to Assert against the errorCodes that has been used in the system under test. 低于测试失败,但我想知道您对如何针对被测系统中使用的errorCode断言的想法。

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

What would be the correct way to test this error codes that has been used in the system? 测试此系统中已使用的错误代码的正确方法是什么?

I suggest to mock only the IRepository and use a real IErrorProvider . 我建议仅模拟IRepository并使用真实的IErrorProvider Then you can call GetErrorList(id) and check the result. 然后,您可以调用GetErrorList(id)并检查结果。

There is not really right or wrong answer and we have decided to use the Assert test as it test the end result. 没有真正正确或错误的答案,我们决定使用Assert测试来测试最终结果。

I took the TDD approach and re-implemented/analysed as below. 我采用了TDD方法,并如下重新进行了实施/分析。

  • Start with a failing test (to simplify the code I removed the Repository from both test and the sut) 从失败的测试开始(为简化代码,我从测试和sut中都删除了存储库)

//AssertTest // 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 // SUT

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

As you would expect the test fail. 如您所料,测试将失败。

Now if write enough production code to make this test pass. 现在,如果编写足够的生产代码以使此测试通过。

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

The VerifyTest would still fail for the above SUT. 上述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));
    }

However if I want this test to pass, I can write the below production code as below 但是,如果我希望此测试通过,则可以编写以下生产代码,如下所示

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

Now the VerifyTest passes, but the AssertTest fails. 现在,VerifyTest通过了,但是AssertTest失败了。

Both tests are valid in their own ways. 两种测试均以其自己的方式有效。 However they test different semantics. 但是,它们测试了不同的语义。 AssertTest test whether the end result contains the correct error codes. AssertTest测试最终结果是否包含正确的错误代码。 Verify test ensures the method is called with the correct error codes. 验证测试可确保使用正确的错误代码调用该方法。 It is important to note that the end value of the Assert test is based on the setup method "match" provided by the Moq framework. 重要的是要注意,Assert测试的最终值基于Moq框架提供的设置方法“ match”。 In other words the setup dictates what the end result would be. 换句话说,设置指示最终结果是什么。 AssertTest would fail if the setup is configured incorrectly or the production code uses error codes that does not match the setup configuration. 如果安装程序配置错误或生产代码使用与安装程序配置不匹配的错误代码,则AssertTest将失败。

It is preferred to use the AssertTest as it test the end result. 最好使用AssertTest来测试最终结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM