如何测试在异步任务方法之前调用的同步代码?
How do I test synchronous code called before an async Task method?
我正在尝试测试一个 async Task
方法,该方法在调用单独的 async Task
方法之前和之后调用同步代码。同步代码更新一个加载状态枚举,它告诉视图层要显示什么(例如加载时的加载微调器,抛出异常时的错误消息等)。请注意,为了简洁起见,我在此示例中省略了 属性 更改事件。
namespace Models
{
public class DataRepository
{
private readonly IDataService dataService;
public DataRepository(IDataService dataService)
{
this.dataService = dataService;
}
public LoadingState LoadingState { get; set; }
public async Task Refresh()
{
// Synchronous code to notify view to show loading state
this.LoadingState = LoadingState.Busy;
try
{
await this.dataService.LoadData();
// Synchronous code to notify view to show done state
this.LoadingState = LoadingState.Done;
}
catch (Exception e)
{
// Synchronous code to notify view to show error state
this.LoadingState = LoadingState.Error;
}
}
}
// This is in a separate class in the actual code. Including here for context.
public enum LoadingState
{
Busy,
Error,
Done,
}
}
我正在使用 NSubstitute to mock IDataService
, XUnit to run the tests, and Fluent Assertions 断言。我可以在以下情况下测试加载状态值:
- 当
LoadData()
方法成功完成时:
[Fact]
public async void Refresh_Success_ExpectLoadingStateDone()
{
var mockDataService = Substitute.For<IDataService>();
var dataRepository = new DataRepository(mockDataService);
await dataRepository.Refresh();
dataRepository.LoadingState.Should().Be(LoadingState.Done);
}
- 当
LoadData()
方法抛出异常时:
[Fact]
public async void Refresh_ThrowsException_ExpectLoadingStateError()
{
var mockDataService = Substitute.For<IDataService>();
mockDataService.Throws<Exception>();
var dataRepository = new DataRepository(mockDataService);
await dataRepository.Refresh();
dataRepository.LoadingState.Should().Be(LoadingState.Error);
}
但是,我无法编写断言加载状态已设置为“忙碌”的测试。
[Fact]
public async void Refresh_InitialState_ExpectLoadingStateBusy()
{
var mockDataService = Substitute.For<IDataService>();
var dataRepository = new DataRepository(mockDataService);
await dataRepository.Refresh();
// This test fails because the loading state is set to "Done" at this point.
dataRepository.LoadingState.Should().Be(LoadingState.Busy);
}
如何为初始加载状态编写测试?有没有办法暂停对 LoadData()
的调用,以便我可以断言初始加载状态?
您可以将 LoadData
模拟为 return 由您管理的任务,当您这样说时它就会完成。然后,不要 await Refresh
因为对于这个测试你不需要 Refresh
来完成,你想要检查正在进行的状态。然后在您调用 Refresh
(没有 await
)后,LoadData
将进行,您可以验证状态为 Busy
。然后完成 LoadData
并等待 Refresh
完成以完成测试。例如:
var mockDataService = Substitute.For<IDataService>();
// this will represent our LoadData in progress
// it won't complete until we tell it
var inProgressTask = new TaskCompletionSource();
// mock LoadData to return this task
mockDataService.Configure().LoadData().Returns(inProgressTask.Task);
var dataRepository = new DataRepository(mockDataService);
// now, execute Refresh but do not await it yet
var pendingRefresh = dataRepository.Refresh();
// assert that state is Busy at this point, as it should be
var state = dataRepository.LoadingState; // Busy
// signal LoadData to complete
inProgressTask.SetResult();
// await Refresh
await pendingRefresh;
我正在尝试测试一个 async Task
方法,该方法在调用单独的 async Task
方法之前和之后调用同步代码。同步代码更新一个加载状态枚举,它告诉视图层要显示什么(例如加载时的加载微调器,抛出异常时的错误消息等)。请注意,为了简洁起见,我在此示例中省略了 属性 更改事件。
namespace Models
{
public class DataRepository
{
private readonly IDataService dataService;
public DataRepository(IDataService dataService)
{
this.dataService = dataService;
}
public LoadingState LoadingState { get; set; }
public async Task Refresh()
{
// Synchronous code to notify view to show loading state
this.LoadingState = LoadingState.Busy;
try
{
await this.dataService.LoadData();
// Synchronous code to notify view to show done state
this.LoadingState = LoadingState.Done;
}
catch (Exception e)
{
// Synchronous code to notify view to show error state
this.LoadingState = LoadingState.Error;
}
}
}
// This is in a separate class in the actual code. Including here for context.
public enum LoadingState
{
Busy,
Error,
Done,
}
}
我正在使用 NSubstitute to mock IDataService
, XUnit to run the tests, and Fluent Assertions 断言。我可以在以下情况下测试加载状态值:
- 当
LoadData()
方法成功完成时:
[Fact]
public async void Refresh_Success_ExpectLoadingStateDone()
{
var mockDataService = Substitute.For<IDataService>();
var dataRepository = new DataRepository(mockDataService);
await dataRepository.Refresh();
dataRepository.LoadingState.Should().Be(LoadingState.Done);
}
- 当
LoadData()
方法抛出异常时:
[Fact]
public async void Refresh_ThrowsException_ExpectLoadingStateError()
{
var mockDataService = Substitute.For<IDataService>();
mockDataService.Throws<Exception>();
var dataRepository = new DataRepository(mockDataService);
await dataRepository.Refresh();
dataRepository.LoadingState.Should().Be(LoadingState.Error);
}
但是,我无法编写断言加载状态已设置为“忙碌”的测试。
[Fact]
public async void Refresh_InitialState_ExpectLoadingStateBusy()
{
var mockDataService = Substitute.For<IDataService>();
var dataRepository = new DataRepository(mockDataService);
await dataRepository.Refresh();
// This test fails because the loading state is set to "Done" at this point.
dataRepository.LoadingState.Should().Be(LoadingState.Busy);
}
如何为初始加载状态编写测试?有没有办法暂停对 LoadData()
的调用,以便我可以断言初始加载状态?
您可以将 LoadData
模拟为 return 由您管理的任务,当您这样说时它就会完成。然后,不要 await Refresh
因为对于这个测试你不需要 Refresh
来完成,你想要检查正在进行的状态。然后在您调用 Refresh
(没有 await
)后,LoadData
将进行,您可以验证状态为 Busy
。然后完成 LoadData
并等待 Refresh
完成以完成测试。例如:
var mockDataService = Substitute.For<IDataService>();
// this will represent our LoadData in progress
// it won't complete until we tell it
var inProgressTask = new TaskCompletionSource();
// mock LoadData to return this task
mockDataService.Configure().LoadData().Returns(inProgressTask.Task);
var dataRepository = new DataRepository(mockDataService);
// now, execute Refresh but do not await it yet
var pendingRefresh = dataRepository.Refresh();
// assert that state is Busy at this point, as it should be
var state = dataRepository.LoadingState; // Busy
// signal LoadData to complete
inProgressTask.SetResult();
// await Refresh
await pendingRefresh;