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
FakeTimeProvider
implementation from NuGet instead of rolling your own? https://devblogs.microsoft.com/dotnet/fake-it-til-you-make-it-to-production/