如何保持单元测试DRY和减少断言

时间:2015-11-18 18:15:14

标签: c# tdd nunit moq autofixture

我正在尝试使用TDD方法实现webserviceclass,该方法发出一堆webrequests并解释响应。我将webrequests封装在几个接口中,以便我可以轻松地模拟它们。通过webserviceclass请求某些内容时,实现的方法始终返回包含错误对象的特定响应对象。借助此错误对象,用户可以确定请求是否成功,以及特定错误是什么。

在写完一堆测试后,我意识到我在 Arrange 阶段重复了很多次:

var mock = new Mock<ISomeWebservices>();
var sut = new MyWebServiceClass(mock.Object);
mock.Setup(foo=>foo.SomeRequest(someData)).Returns(@"{""user"": ""12345"",""somedata"": ""60"",""someotherdata"":""2015-09-01T12:00:00.200Z""}");
sut.SomeRequest(someData,s=> response = s);

前两行对于所有测试始终相同。模拟器总是有一个返回设置或抛出异常的设置。根据我测试的要求,我必须设置不同的课程方法。 我尝试使用 Autofixture 解决这个问题,以便我可以为每个webrequest编写ICustomization,但问题是我仍然需要访问模拟以获得特定于测试的安装程序。

另一个问题是 Assert 阶段。因为我总是从请求中获取一个错误对象,所以如果我遇到了错误和错误,我只会声明错误对象。

    Assert.That(response.Error.Type,Is.EqualTo(ErrorInfo.ErrorType.IllegalToken));
    Assert.That(response.Error.Message, Is.Not.Null);
    Assert.That(response.Error.AdvisedAction, Is.Not.Null);
    Assert.That(response.Error.RawData, Is.Not.Null);

因此,如果存在错误,则错误对象的某些属性应始终填写,有些属性可以。 (例如,如果错误是由异常触发的,则异常属性不应为null等) 据我所知,在单元测试中有几个断言是一种不好的做法,所以我想尽可能避免这种情况。

[编辑] 根据评论,有多个Asserts以及重复我提到的排列部分,并不是那么糟糕。所以我不会使用Autofixture。

2 个答案:

答案 0 :(得分:4)

您当然可以使用AutoFixture来减少 Arrange 阶段所需的样板代码量。它看起来像这样:

[Theory, AutoMoqData]
public void Test(
    [Frozen]Mock<ISomeWebservices> mock,
    MyWebServiceClass sut,
    object someData,
    object response)
{
    mock.Setup(foo => foo.SomeRequest(someData)).Returns(@"{""user"": ""12345"",""somedata"": ""60"",""someotherdata"":""2015-09-01T12:00:00.200Z""}");
    sut.SomeRequest(someData, s => response = s);
    // Assertions go here...
}

在这里,我不得不猜测someDataresponse的类型,但如果它们与object不同,则只需声明它们属于该类型

在上面的示例中,[AutoMoqData]属性的定义如下:

public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute() :
        base(new Fixture().Customize(new AutoMoqCustomization()))
    {
    }
}

所有其他类型和AutoFixture功能都来自AutoFixture.Xunit2AutoFixture.AutoMoq NuGet包。

当谈到多个断言时,我同意其他评论者认为它看起来并不那么糟糕,但本着GOOS的精神,你应该听取你的测试。在这种特殊情况下,测试似乎说: response的等式比较看起来很重要。如果是这样的话,你可以考虑覆盖{{ 1}}在Equals上,并为其提供结构平等,而不是参考平等

答案 1 :(得分:3)

您希望尽可能避免重复,理想情况下应将测试代码写入生产代码标准,因为您可以花费尽可能多的时间来阅读和维护测试代码作为生产代码。获得平衡是关键,因为你不需要像指出的那样进行脆弱的测试,而不是相互依赖。

通常使用测试数据构建器来简化和删除创建测试数据时的重复数据。对象。如果您更改对象的创建方式,这也会有所帮助,因为您只更新一个构建器方法而不是所有单个测试。

每次测试多个断言都很好(可能是必不可少的),只需确保断言是相关的,并且测试坚持单一概念,有助于快速识别错误。