为了账号安全,请及时绑定邮箱和手机立即绑定

模拟界面没有意义?

模拟界面没有意义?

C#
宝慕林4294392 2023-04-16 10:11:37
我是单元测试的新手,感觉我在这里遗漏了一些非常重要的东西。我想在下面测试 DoSomethingWithArray 的结果:class Traffic:ITraffic{    private HugeArray _hugeArray;    public Traffic(HugeArray hugeArray)    {        _hugeArray = hugeArray;    }    public int DoSomethingWithArray()    {        var ret = 0;        //Here some code that does something with big array and stores some integer values in ret        return ret;    }}class HugeArray{    //This is my custom data structure;}interface ITraffic{    int DoSomethingWithArray();}我正在使用 Nunit,从我读到的内容来看,模拟接口比模拟类更好。我的问题是,我想测试的是 DosomethingWithArray 在 Traffic 类中的具体功能,我很难概念化 ITraffic 接口如何适应。我在这里缺少什么?编辑这是我将如何测试我的课程[TestFixture]public class TrafficTests{    private Traffic _traffic;    private const int size = 1000000;    private const int key = 1851925790;    [OneTimeSetUp]    public void Setup()    {        var hugeArray = new HugeArray(size);        //Some Setups to create an edge case, not  relevant to my question        hugeArray.AddValue(size - 1, Int.MaxValue);        hugeArray.AddValue(size - 2, key);        //This is the object I want to test,         _traffic = new Traffic(hugeArray);    }    [Test]    public void DoSomethingWithArray_Test()    {        Assert.DoesNotThrow(() =>                            {                                var ret = _traffic.DoSomethingWithArray();                                Assert.AreEqual(ret, 233398);                            });    }} 我的问题是:这种方法看起来正确吗?为测试创建的对象是否正常,或者我应该模拟 ITraffic 接口吗?
查看完整描述

1 回答

?
白衣染霜花

TA贡献1796条经验 获得超10个赞

在您的示例中,您正在测试Traffic. 实现无关紧要Traffic。ITraffic如果您: ITraffic从类中删除,则它不再实现该接口,它根本不会改变您测试的方式Traffic。


你在测试Traffic。我们不会嘲笑我们正在测试的东西。我们嘲笑我们没有测试的东西。


假设我有这个验证地址的类:


public class AddressValidator

{

    public ValidationResult ValidateAddress(Address address)

    {

        var result = new ValidationResult();


        if(string.IsNullOrEmpty(address.Line1))

            result.AddError("Address line 1 is empty.");

        if(string.IsNullOrEmpty(address.City))

            result.AddError("The city is empty.");


        // more validations


        return result;

    }

}

这个类是否实现接口并不重要。如果我正在测试此类,则没有什么可嘲笑的。


假设我意识到我还需要验证邮政编码,但为此我可能需要查询一些外部数据以查看城市是否与邮政编码匹配。可能不同的国家不一样。所以我写了一个新的接口并将其注入到这个类中:


public interface IPostalCodeValidator

{

    ValidationResult ValidatePostalCode(Address address);

}


public class AddressValidator

{

    private readonly IPostalCodeValidator _postalCodeValidator;


    public AddressValidator(IPostalCodeValidator postalCodeValidator)

    {

        _postalCodeValidator = postalCodeValidator;

    }


    public ValidationResult ValidateAddress(Address address)

    {

        var result = new ValidationResult();


        if (string.IsNullOrEmpty(address.Line1))

            result.AddError("Address line 1 is empty.");

        if (string.IsNullOrEmpty(address.City))

            result.AddError("The city is empty.");


        var postalCodeValidation = _postalCodeValidator.ValidatePostalCode(address);

        if (postalCodeValidation.HasErrors)

            result.AddErrors(postalCodeValidation.Errors);


        return result;

    }

}

邮政编码验证非常复杂,它将在自己的类中进行自己的测试。当我们测试时,AddressValidator我们不想测试邮政编码验证器。我们只想隔离测试这个类,单独测试另一个类。想要AddressValidator确保它_postalCodeValidator.ValidatePostalCode被调用,并且如果它返回错误,我们将它们添加到验证结果中。


我们不在这里测试IPostalCodeValidator(或其实现),所以我们模拟它。例如,使用最小起订量:


public void AddressValidator_adds_postal_code_errors()

{

    var postalCodeError = new ValidationResult();

    postalCodeError.AddError("Bad!");

    postalCodeError.AddError("Worse!");


    var postalCodeValidatorMock = new Mock<IPostalCodeValidator>();

    postalCodeValidatorMock.Setup(x => x.ValidatePostalCode(It.IsAny<Address>()))

        .Returns(postalCodeError);


    var subject = new AddressValidator(postalCodeValidatorMock.Object);

    var result = subject.ValidateAddress(new Address());


    Assert.IsTrue(result.Errors.Contains("Bad!"));

    Assert.IsTrue(result.Errors.Contains("Worse!"));

}

我们实际上并没有验证邮政编码。我们只是说,为了测试,邮政编码验证器总是会返回这两个错误。然后我们确保AddressValidator调用它并执行我们期望它对这些错误执行的操作。


这基本上就是模拟的内容。这是一个伪造的实现,它做一些简单的事情,比如罐头响应,这样我们就可以确保我们按照我们期望的方式处理罐头响应。如果AddressValidator正确处理结果,那么它就可以正常工作。完成。


为了确保真正的邮政编码验证器返回正确的结果,我们可以为该类编写测试。这样,每个班级都会做一些简单的事情,并进行测试以确保它正确地做事。当我们将它们放在一起时,整个事情更有可能奏效。如果我们破坏的实现IPostalCodeValidator,那么该类的测试将失败,但 的测试AddressValidator仍然会通过。这样我们就可以快速了解哪个部分出了问题,因为它们都是单独测试的,所以我们不必运行和调试大量代码来试图找出问题所在。


查看完整回答
反对 回复 2023-04-16
  • 1 回答
  • 0 关注
  • 83 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信