使用自动修复功能生成副本

问题描述 投票:1回答:1

我正在使用Autofixture进行单元测试,并带有自动生成的数据。

为了测试一个简单的Controller端点(按ID获取员工),我正在做类似的事情:

[Theory, AutoDomainData]
public void GetEmployeeById_ValidId_ReturnsExpectedModel(
    EmployeeModel expectedEmployee,
    [Frozen] Mock<IEmployeeService> employeeServiceMock,
    EmployeesController sut)
{
    employeeServiceMock
        .Setup(x => x.GetEmployeeById(42))
        .Returns(expectedEmployee);

    var actual = sut.GetEmployeeById(42);

    actual.As<OkObjectResult>().Value.As<EmployeeModel>()
        .Should().BeEquivalentTo(expectedEmployee);
}

和控制器:

[HttpGet("{id:int}")]
public IActionResult GetEmployeeById(int id)
{
    var employee = employeeService.GetEmployeeById(id);
    if (employee == null)
        return NotFound("Employee not found");

    return Ok(employee);
}

在此单元测试中,expectedEmployee是使用“随机”数据自动生成的。 sut(被测系统)被配置为以其所有必需的依赖项生成(其中之一是IEmplyeeService)。

此单元测试的问题在于,如果我在从控制器退回员工之前更换了雇员,则该测试仍将通过(因为它引用了同一对象):

employee.SomeInternalModel.FooProperty = "Foo";
return Ok(employee);

因此,我认为上述单元测试很糟糕。

为了使单元测试在这种情况下失败,我需要传递一个单独的对象:EmployeeModel的深层副本:

employeeServiceMock
    .Setup(x => x.GetEmployeeById(42))
    .Returns(expectedEmployee.DeepCopy());

我没有时间和资源来为我的所有模型编写深度复制方法。

如何轻松地自动生成相同的模型?我考虑过为AutoFixture播种,但似乎不支持此功能。

您有什么优雅建议吗?

c# unit-testing asp.net-core autofixture
1个回答
1
投票

我认为您需要提出问题什么您正在测试?在您的testcase中,您仅测试SUT是否返回由employee返回的service。 IMO是否是同一实例都没有关系。更新属性不应破坏this测试。

但是您要解决一个更广泛的问题,在其他情况下,您确实希望将expectedactual乘以structural equality进行比较,在这种情况下,您可以(例如,使用xUnit s MemberDatabuilder会在您调用两次时生成两个实例:

var employee = new EmployeeModelBuilder().Build();

可以使用With()方法增强构建器:

var employee = new EmployeeModelBuilder().With(name: "John").Build();

或者您可以只使用new EmployeeModel {}内联创建这些对象。

结构相等意味着您需要一个覆盖Equality成员或在断言中使用IEqualityComparer<>的对象。

更新

[如果您不想使用自定义构建器(如您所说),则可以指示AutoFixture生成具有设置为值的特定属性的对象。如果然后要求它创建一个实例twice(一次用于expected,一次创建由注入到SUT中的服务返回的实例),则可以将expectedactual您的Assert阶段。

    [Fact]
    public void Sut_ReturnsEmployee_FromService()
    {
        var fixture = new Fixture();
        fixture.Customize<EmployeeModel>(e => e.With(x => x.Name, "Foo"));
        var expected = fixture.Create<EmployeeModel>();

        var foundEmployee = fixture.Create<EmployeeModel>();
        var employeeServiceMock = new Mock<IEmployeeService>();
        employeeServiceMock.Setup(f => f.GetEmployeeById(42)).Returns(foundEmployee);

        var sut = new EmployeeController(employeeServiceMock.Object);

        var actual = sut.GetEmployeeById(42);

        Assert.Equal(expected.Name, actual.Name);
    }

这里我使用了[Fact],并且断言比较了两个特定的属性是否相等,但是当您比较结构相等时,您可以仅比较对象本身(如上所述)。现在,您可以验证SUT返回期望的实例,而不会篡改and而不使用对同一实例的两个引用。

© www.soinside.com 2019 - 2024. All rights reserved.