单元测试基于状态的测试
Unit testing state based test
假设我有一个名为 CreateApplication
的方法和一个私有辅助方法 GenerateApplication
,它生成带有计算字段的应用程序对象。之后,我将生成的应用程序对象插入到数据库中。
public async Task<Guid> CreateApplication(CreateApplicationdRequest request)
{
var application = GenerateApplication(request);
await UnitOfWork.Application.InsertAsync(application); // it's same as _db.Application.InsertAsync(application)
await UnitOfWork.CommitAsync(); // it's same as _db.SaveChangesAsync();
return application.Id;
}
对于这种情况,什么是合适的单元测试?我是否应该验证 InsertAsync
方法(将应用程序插入数据库)并检查应用程序对象是否正确生成?
像这样:
[Fact]
public async Task CreateApplication_WhenCalled_ShouldStoreApplication()
{
// I have already mocked unitofwork in constructor
// when InsertAsync method will called, it will assign passed argument to declared private field (application)
Application application;
UnitOfWork.Setup(x => x.Application.InsertAsync(It.IsAny<Application>()))
.Callback<Application>(app => application = app);
//act
await _applicationService.CreateApplication(new CreateApplicationdRequest()); // dummy request
//assert
UnitOfWork.Verify(x => x.Application.InsertAsync(It.IsAny<Application>()), Times.Once);
UnitOfWork.Verify(x => x.CommitAsync(), Times.Once);
//here I am checking generated application's state
Assert.Equal(application.CustomerId, request.CustomerId);
Assert.Equal(application.Applicant.PhoneNumber, request.PhoneNumber);
Assert.Equal(application.LoanCurrencyId, request.LoanCurrencyId);
Assert.Equal(application.TermType, request.TermType);
Assert.Equal(application.Term, request.Term);
Assert.Equal("01017054322_CC-01001", application.DocumentNo);
Assert.Equal(application.GracePeriod, request.DefaultGracePeriod);
}
或者这不是正确的方法,对于这种情况最好使用集成测试?
有几种方法可以对方法进行单元测试。最流行的两种是黑盒和白盒测试。
黑盒测试
您对实现一无所知。您只关心输出(包括 return 值、输出参数和 side-effects)。因此,您想确保无论何时使用给定输入调用它,您都会看到预期的输出。
在那种情况下,您的测试将只检查 returned Guid 并验证应用程序是否存储在模拟存储中。
Assert.NotEqual(result, Guid.Empty);
Assert.NotNull(x.Application.FirstOrDefault(app => app.Id == result));
这种方法可以真正轻松地扩展到集成测试,而不是使用模拟存储,您可以使用存储的沙盒版本。
白盒测试
你知道你的方法是如何实现的。并且您要确保您的代码不会执行任何不需要的操作,例如在 GenerateApplication
和 InsertAsync
方法调用之间覆盖应用程序的某些属性。
这可能是好事也可能是坏事,这取决于您如何看待它。假设您需要规范化 PhoneNumber
并且出于任何原因在 CreateApplication
中执行此操作。从黑盒的角度来看,这无关紧要(无需更新您的测试),因为功能没有改变。从白盒角度来看,您需要调整测试以模拟规范化器并验证其调用并断言预期输出。
前者从回归的角度可以看作是安全网,而后者可以看作是重构的安全网。
假设我有一个名为 CreateApplication
的方法和一个私有辅助方法 GenerateApplication
,它生成带有计算字段的应用程序对象。之后,我将生成的应用程序对象插入到数据库中。
public async Task<Guid> CreateApplication(CreateApplicationdRequest request)
{
var application = GenerateApplication(request);
await UnitOfWork.Application.InsertAsync(application); // it's same as _db.Application.InsertAsync(application)
await UnitOfWork.CommitAsync(); // it's same as _db.SaveChangesAsync();
return application.Id;
}
对于这种情况,什么是合适的单元测试?我是否应该验证 InsertAsync
方法(将应用程序插入数据库)并检查应用程序对象是否正确生成?
像这样:
[Fact]
public async Task CreateApplication_WhenCalled_ShouldStoreApplication()
{
// I have already mocked unitofwork in constructor
// when InsertAsync method will called, it will assign passed argument to declared private field (application)
Application application;
UnitOfWork.Setup(x => x.Application.InsertAsync(It.IsAny<Application>()))
.Callback<Application>(app => application = app);
//act
await _applicationService.CreateApplication(new CreateApplicationdRequest()); // dummy request
//assert
UnitOfWork.Verify(x => x.Application.InsertAsync(It.IsAny<Application>()), Times.Once);
UnitOfWork.Verify(x => x.CommitAsync(), Times.Once);
//here I am checking generated application's state
Assert.Equal(application.CustomerId, request.CustomerId);
Assert.Equal(application.Applicant.PhoneNumber, request.PhoneNumber);
Assert.Equal(application.LoanCurrencyId, request.LoanCurrencyId);
Assert.Equal(application.TermType, request.TermType);
Assert.Equal(application.Term, request.Term);
Assert.Equal("01017054322_CC-01001", application.DocumentNo);
Assert.Equal(application.GracePeriod, request.DefaultGracePeriod);
}
或者这不是正确的方法,对于这种情况最好使用集成测试?
有几种方法可以对方法进行单元测试。最流行的两种是黑盒和白盒测试。
黑盒测试
您对实现一无所知。您只关心输出(包括 return 值、输出参数和 side-effects)。因此,您想确保无论何时使用给定输入调用它,您都会看到预期的输出。
在那种情况下,您的测试将只检查 returned Guid 并验证应用程序是否存储在模拟存储中。
Assert.NotEqual(result, Guid.Empty);
Assert.NotNull(x.Application.FirstOrDefault(app => app.Id == result));
这种方法可以真正轻松地扩展到集成测试,而不是使用模拟存储,您可以使用存储的沙盒版本。
白盒测试
你知道你的方法是如何实现的。并且您要确保您的代码不会执行任何不需要的操作,例如在 GenerateApplication
和 InsertAsync
方法调用之间覆盖应用程序的某些属性。
这可能是好事也可能是坏事,这取决于您如何看待它。假设您需要规范化 PhoneNumber
并且出于任何原因在 CreateApplication
中执行此操作。从黑盒的角度来看,这无关紧要(无需更新您的测试),因为功能没有改变。从白盒角度来看,您需要调整测试以模拟规范化器并验证其调用并断言预期输出。
前者从回归的角度可以看作是安全网,而后者可以看作是重构的安全网。