How to assert timer callback in c# xunit unit test #53146
-
| I have a  
 public class OfflineHourlyDatabaseBackup(
	ILogger<OfflineHourlyDatabaseBackup> logger,
	TimeProvider timeProvider,
	IServiceProvider serviceProvider) : BackgroundService
{
	protected override async Task ExecuteAsync(CancellationToken stoppingToken)
	{
		await Task.Yield();
		timeProvider.CreateTimer(Backup, null, TimeSpan.Zero, TimeSpan.FromHours(1));
		stoppingToken.Register(() => logger.LogWarning($"{nameof(OfflineHourlyDatabaseBackup)} is stopping due to host shut down."));
	}
	private void Backup(object? state)
	{
		using var scope = serviceProvider.CreateScope();
		var offlineDatabaseBackupService = scope.ServiceProvider.GetRequiredService<IOfflineDatabaseBackupService>();
		offlineDatabaseBackupService.Backup();
	}
}I have written uni test to test the behavior. Here is my  [Fact]
public async Task OfflineHourlyDatabaseBackup_ExecuteAsync()
{
	// Arrange
	var serviceCollection = new ServiceCollection();
	var mockOfflineDatabaseBackupService = new Mock<IOfflineDatabaseBackupService>();
	serviceCollection.AddSingleton(_ => mockOfflineDatabaseBackupService.Object);
	var now = DateTimeOffset.UtcNow;
	var timeProvider = new TestTimeProvider(now);
	var serviceProvider = serviceCollection.BuildServiceProvider();
	var logger = NullLogger<OfflineHourlyDatabaseBackup>.Instance;
	var configuration = new Mock<IConfiguration>();
	// Act
	using var offlineHourlyDatabaseBackup = new OfflineHourlyDatabaseBackup(logger, timeProvider, serviceProvider);
	await offlineHourlyDatabaseBackup.StartAsync(default);
	await offlineHourlyDatabaseBackup.ExecuteTask!; // --> Backup() should be called
	timeProvider.Advance(TimeSpan.FromHours(1)); // --> Backup() should be called
	// Assert
	offlineHourlyDatabaseBackup.ExecuteTask.IsCompletedSuccessfully.Should().BeTrue();
	mockOfflineDatabaseBackupService.Verify(x => x.Backup(), Times.AtLeast(2));
}Here is my  internal class TestTimeProvider(DateTimeOffset now) : TimeProvider
{
	public override DateTimeOffset GetUtcNow() => now;
	public override ITimer CreateTimer(TimerCallback callback, object? state, TimeSpan dueTime, TimeSpan period)
	{
		return base.CreateTimer(callback, state, TimeSpan.Zero, TimeSpan.FromHours(1));
	}
	public void Advance(TimeSpan timeSpan)
	{
		now = now.Add(timeSpan);
	}
}When I run the test I expect  
 Upon debugging I have noticed that before the first  Please can you assist me on what I'm doing wrong here ? | 
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
| Have you considered using the  | 
Beta Was this translation helpful? Give feedback.
-
| @martincostello Many thanks that helped and I solved the issue. | 
Beta Was this translation helpful? Give feedback.
Have you considered using the
FakeTimeProviderimplementation from NuGet instead of rolling your own? https://devblogs.microsoft.com/dotnet/fake-it-til-you-make-it-to-production/