Skip to content

Commit

Permalink
Removed tests that use NSubstitute
Browse files Browse the repository at this point in the history
  • Loading branch information
NielsPilgaard committed Dec 3, 2022
1 parent 45502e4 commit 8e9c9c7
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 162 deletions.
98 changes: 28 additions & 70 deletions tests/Pilgaard.CronJobs.Tests/CronBackgroundServiceTests.cs
Original file line number Diff line number Diff line change
@@ -1,127 +1,85 @@
using Cronos;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NSubstitute;
using NSubstitute.Exceptions;
using NSubstitute.ReceivedExtensions;
using Microsoft.Extensions.Logging.Abstractions;
using Pilgaard.CronJobs.Configuration;
using Pilgaard.CronJobs.Extensions;
using Xunit;

namespace Pilgaard.CronJobs.Tests;

public class CronBackgroundServiceTests : IAsyncLifetime
{
private readonly IServiceScopeFactory _serviceScopeFactoryMock;
private readonly ILogger<CronBackgroundService> _loggerMock;
private readonly ICronJob _cronJobMock;
private readonly IServiceScope _serviceScopeMock;
private readonly IServiceProvider _serviceProviderMock;
private readonly CronJobOptions _options;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly ILogger<CronBackgroundService> _logger;
private readonly TestCronJob _cronJob;
private readonly ServiceProvider _serviceProvider;

private readonly CronJobOptions _options;
private CancellationTokenSource? _cts;
private CronBackgroundService? _sut;

public CronBackgroundServiceTests()
{
_serviceScopeFactoryMock = Substitute.For<IServiceScopeFactory>();
_serviceScopeMock = Substitute.For<IServiceScope>();
_serviceProviderMock = Substitute.For<IServiceProvider>();
_loggerMock = Substitute.For<ILogger<CronBackgroundService>>();
_cronJobMock = Substitute.For<ICronJob>();
_options = new CronJobOptions();

var services = new ServiceCollection()
.AddCronJobs(options => options.ServiceLifetime = ServiceLifetime.Singleton,
typeof(CronBackgroundServiceTests));

_serviceProvider = services.BuildServiceProvider();
_cronJob = _serviceProvider.GetRequiredService<TestCronJob>();
_serviceScopeFactory = _serviceProvider.GetRequiredService<IServiceScopeFactory>();
_logger = new NullLogger<CronBackgroundService>();
}

[Fact]
public async Task When_CronBackgroundService_IsRunning_ItsCronJob_IsExecuted()
{
// Arrange
RunCronJobEverySecond();

// Act
await Task.Delay(TimeSpan.FromSeconds(3));

// Assert that ExecuteAsync has been received at least once, after 3 seconds.
await AssertWithRetryAsync(async () =>
await _cronJobMock.Received(Quantity.AtLeastOne()).ExecuteAsync(Arg.Any<CancellationToken>()),
TimeSpan.FromSeconds(3));
// Assert - ExecuteAsync has been received at least once, after 3 seconds.
_cronJob.PersistentField.Should().BeGreaterThanOrEqualTo(1);
}

/// <summary>
/// This test is flaky, there's no way of knowing for how long it'll run on GitHub Actions.
/// </summary>
[Fact]
public async Task When_CronBackgroundService_IsRunning_ItsCronJob_IsExecuted_TheRightNumberOfTimes()
{
// Arrange
RunCronJobEverySecond();

// Act
await Task.Delay(TimeSpan.FromSeconds(5));

// Assert that ExecuteAsync has been received at least 5 times, after 5 seconds.
// In CI, this might span over far more than 5 seconds, so the quantity just has to be 5-30
await AssertWithRetryAsync(async () =>
await _cronJobMock.Received(Quantity.Within(5, 30)).ExecuteAsync(Arg.Any<CancellationToken>()),
TimeSpan.FromSeconds(5));
// Assert - ExecuteAsync has been received at least 5 times, after 5 seconds.
_cronJob.PersistentField.Should().BeGreaterThanOrEqualTo(5);
}



[Fact]
public async Task When_CronBackgroundService_IsRunning_ItsCronJob_IsNotExecuted_MoreThanItShould()
{
// Arrange
RunCronJobEveryMinute();

// Act
await Task.Delay(TimeSpan.FromSeconds(2));

// Assert that ExecuteAsync has not been received at least 5 times, after 2 seconds.
await Assert.ThrowsAsync<ReceivedCallsException>(async () =>
await _cronJobMock.Received(5).ExecuteAsync(Arg.Any<CancellationToken>()));
// Assert
_cronJob.PersistentField.Should().BeInRange(1, 5);
}

private void MockCronJobAndServiceScope(ICronJob cronJob)
{
_serviceScopeFactoryMock.CreateScope().Returns(_serviceScopeMock);
_serviceScopeMock.ServiceProvider.Returns(_serviceProviderMock);
_serviceProviderMock
.GetService(cronJob.GetType())
.ReturnsForAnyArgs(cronJob);
}

private static async Task AssertWithRetryAsync(Func<Task> assertion, TimeSpan timeout)
{
using var cts = new CancellationTokenSource(timeout);
var token = cts.Token;

while (!token.IsCancellationRequested)
{
try
{
await assertion.Invoke();
return;
}
catch (Exception ex)
{
Console.WriteLine(ex);
await Task.Delay(100, token);
}
}
}

private void RunCronJobEverySecond() => _cronJobMock.CronSchedule.Returns(CronExpression.Parse("* * * * * *", CronFormat.IncludeSeconds));
private void RunCronJobEveryMinute() => _cronJobMock.CronSchedule.Returns(CronExpression.Parse("* * * * *"));

public async Task InitializeAsync()
{
MockCronJobAndServiceScope(_cronJobMock);
_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
_sut = new CronBackgroundService(_cronJobMock, _serviceScopeFactoryMock, _loggerMock, _options);
await _sut.StartAsync(_cts!.Token);
_sut = new CronBackgroundService(_cronJob, _serviceScopeFactory, _logger, _options);
await _sut.StartAsync(_cts.Token);
}

public async Task DisposeAsync()
{
await _sut!.StopAsync(_cts!.Token);
await _serviceProvider.DisposeAsync();
_cts.Cancel();
_cts.Dispose();
}
Expand Down
7 changes: 1 addition & 6 deletions tests/Pilgaard.CronJobs.Tests/Pilgaard.CronJobs.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(NetVersion)</TargetFramework>
Expand All @@ -15,11 +15,6 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="NSubstitute" Version="4.4.0" />
<PackageReference Include="NSubstitute.Analyzers.CSharp" Version="1.0.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
86 changes: 0 additions & 86 deletions tests/Pilgaard.CronJobs.Tests/RealCronBackgroundServiceTests.cs

This file was deleted.

0 comments on commit 8e9c9c7

Please sign in to comment.