c# - 使用 Autofixture 生成副本

标签 c# unit-testing asp.net-core autofixture

我使用 Autofixture 进行单元测试,使用自动生成的数据。

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

[Theory, AutoData]
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);
}

和 Controller :

[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)。

这个单元测试的问题是,如果我在从 Controller 返回员工之前更改它,测试仍然会通过(因为它引用了同一个对象):

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

所以,我认为上面的单元测试很糟糕。

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

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

我没有时间和资源为我的所有模型编写深拷贝方法。

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

你有什么优雅的建议吗?

最佳答案

我想你需要问这个问题你在测试什么?在您的测试用例中,您只测试SUT 是否返回 employeeservice 返回; IMO它是否是同一个实例并不重要。更新属性不应破坏这个测试。

虽然你触及了一个更广泛的问题,但在其他情况下你真的想比较你的 expectedactual通过 structural equality在这种情况下,您可以(例如使用 xUnit s MemberData)使用 builder当你调用它两次时它会生成两个实例:

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

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

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

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

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

更新

如果您不想使用自定义构建器(如您所说),您可以指示 AutoFixture生成具有特定属性集的对象。如果您随后要求它创建一个实例两次(一次为您的 expected 一次为注入(inject)您的 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返回预期的实例而不篡改而不使用对同一实例的两个引用。

关于c# - 使用 Autofixture 生成副本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58543959/

相关文章:

c# - .netcore PUT 方法 405 方法不允许

linux - 如何使用 .Net Core 编写 linux 守护进程

c# - 在 C# 中引发忽略处理程序引发的异常的事件

unit-testing - 这个设计(反)模式的名称是什么?

c# - 如何模拟返回 void 但修改通过 Moq 传入的引用类型的方法

java - 如何为内存 LDAP 服务器中的 UnboundID 添加/自定义控件

visual-studio - Visual Studio 2017:在已创建的Asp .Net Core Web应用程序上访问更改身份验证屏幕

java - 为什么省略花括号被认为是一种不好的做法?

c# - 乘长值的正确方法

c# - 在 C# 中使用 IEnumerator 从集合中删除元素