我正在使用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
播种,但似乎不支持此功能。
您有什么优雅建议吗?
我认为您需要提出问题什么您正在测试?在您的testcase中,您仅测试SUT
是否返回由employee
返回的service
。 IMO是否是同一实例都没有关系。更新属性不应破坏this测试。
但是您要解决一个更广泛的问题,在其他情况下,您确实希望将expected
与actual
乘以structural equality
进行比较,在这种情况下,您可以(例如,使用xUnit
s MemberData
) builder
会在您调用两次时生成两个实例:
var employee = new EmployeeModelBuilder().Build();
可以使用With()
方法增强构建器:
var employee = new EmployeeModelBuilder().With(name: "John").Build();
或者您可以只使用new EmployeeModel {}
内联创建这些对象。
结构相等意味着您需要一个覆盖Equality
成员或在断言中使用IEqualityComparer<>
的对象。
更新
[如果您不想使用自定义构建器(如您所说),则可以指示AutoFixture
生成具有设置为值的特定属性的对象。如果然后要求它创建一个实例twice(一次用于expected
,一次创建由注入到SUT
中的服务返回的实例),则可以将expected
与actual
您的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而不使用对同一实例的两个引用。