0

下面是我想要测试的一个类(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));
}

但是我更喜欢测试最终结果。例如,我想针对生产代码中使用的错误代码进行断言。以下测试失败,但我想知道您对如何针对被测系统中使用的错误代码进行断言的想法。

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

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

4

2 回答 2

1

我建议只模拟IRepository并使用真实的IErrorProvider. 然后你可以打电话GetErrorList(id)检查结果。

于 2012-11-08T11:31:07.597 回答
0

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

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

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

//断言测试

    [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 仍然会失败。

//验证测试

   [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 框架提供的设置方法“match”。换句话说,设置决定了最终结果。如果设置配置不正确或生产代码使用与设置配置不匹配的错误代码,AssertTest 将失败。

最好使用 AssertTest 来测试最终结果。

于 2012-11-20T11:56:42.697 回答