Тесты фоновых задач

This commit is contained in:
Степанов Дмитрий 2023-11-27 17:47:27 +05:00
parent 4767335900
commit a2fd900cf1
5 changed files with 281 additions and 273 deletions

View File

@ -1,113 +0,0 @@
using AsbCloudInfrastructure.Background;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using System;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace AsbCloudWebApi.Tests.Services;
public class BackgroundWorkerTest
{
private IServiceProvider provider;
private BackgroundWorker service;
public BackgroundWorkerTest()
{
provider = Substitute.For<IServiceProvider, ISupportRequiredService>();
var serviceScope = Substitute.For<IServiceScope>();
var serviceScopeFactory = Substitute.For<IServiceScopeFactory>();
serviceScopeFactory.CreateScope().Returns(serviceScope);
((ISupportRequiredService)provider).GetRequiredService(typeof(IServiceScopeFactory)).Returns(serviceScopeFactory);
service = new BackgroundWorker(provider);
typeof(BackgroundWorker)
.GetField("minDelay", BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(service, TimeSpan.FromMilliseconds(1));
}
[Fact]
public async Task Enqueue_n_works()
{
var workCount = 10;
var result = 0;
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
{
result++;
return Task.Delay(1);
}
//act
for (int i = 0; i < workCount; i++)
{
var work = Work.CreateByDelegate(i.ToString(), workAction);
service.Enqueue(work);
}
await service.ExecuteTask;
//assert
Assert.Equal(workCount, result);
}
[Fact]
public async Task Enqueue_continues_after_exceptions()
{
var expectadResult = 42;
var result = 0;
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
{
result = expectadResult;
return Task.CompletedTask;
}
var goodWork = Work.CreateByDelegate("", workAction);
Task failAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
=> throw new Exception();
var badWork = Work.CreateByDelegate("", failAction);
badWork.OnErrorAsync = (id, exception, token) => throw new Exception();
//act
service.Enqueue(badWork);
service.Enqueue(goodWork);
await service.ExecuteTask;
//assert
Assert.Equal(expectadResult, result);
Assert.Equal(1, service.Felled.Count);
Assert.Equal(1, service.Done.Count);
}
[Fact]
public async Task TryRemove()
{
var workCount = 5;
var result = 0;
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
{
result++;
return Task.Delay(10);
}
//act
for (int i = 0; i < workCount; i++)
{
var work = Work.CreateByDelegate(i.ToString(), workAction);
service.Enqueue(work);
}
var removed = service.TryRemoveFromQueue((workCount - 1).ToString());
await service.ExecuteTask;
//assert
Assert.True(removed);
Assert.Equal(workCount - 1, result);
Assert.Equal(workCount - 1, service.Done.Count);
}
}

View File

@ -1,150 +0,0 @@
using AsbCloudInfrastructure.Background;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using System;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace AsbCloudWebApi.Tests.Services
{
public class WorkTest
{
private IServiceProvider provider;
public WorkTest()
{
provider = Substitute.For<IServiceProvider, ISupportRequiredService>();
var serviceScope = Substitute.For<IServiceScope>();
var serviceScopeFactory = Substitute.For<IServiceScopeFactory>();
serviceScopeFactory.CreateScope().Returns(serviceScope);
((ISupportRequiredService)provider).GetRequiredService(typeof(IServiceScopeFactory)).Returns(serviceScopeFactory);
}
[Fact]
public async Task Work_done_with_success()
{
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
=> Task.CompletedTask;
var work = Work.CreateByDelegate("", workAction);
//act
var begin = DateTime.Now;
await work.Start(provider, CancellationToken.None);
var done = DateTime.Now;
var executionTime = done - begin;
//assert
Assert.Equal(1, work.CountComplete);
Assert.Equal(1, work.CountStart);
Assert.Equal(0, work.CountErrors);
Assert.Null(work.CurrentState);
Assert.Null(work.LastError);
var lastState = work.LastComplete;
Assert.NotNull(lastState);
Assert.InRange(lastState.Start, begin, done - 0.5 * executionTime);
Assert.InRange(lastState.End, done - 0.5 * executionTime, done);
Assert.InRange(lastState.ExecutionTime, TimeSpan.Zero, executionTime);
}
[Fact]
public async Task Work_calls_callback()
{
var expectedState = "42";
var expectedProgress = 42d;
var timeout = TimeSpan.FromMilliseconds(40);
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
{
callback.Invoke(expectedState, expectedProgress);
return Task.Delay(timeout);
}
var work = Work.CreateByDelegate("", workAction);
//act
var begin = DateTime.Now;
_ = work.Start(provider, CancellationToken.None);
await Task.Delay(timeout/3);
//assert
Assert.Equal(0, work.CountComplete);
Assert.Equal(1, work.CountStart);
Assert.Equal(0, work.CountErrors);
Assert.NotNull(work.CurrentState);
Assert.Null(work.LastComplete);
Assert.Null(work.LastError);
var currentState = work.CurrentState;
Assert.NotNull(currentState);
Assert.InRange(currentState.Start, begin, begin + timeout);
Assert.InRange(currentState.StateUpdate, begin, begin + timeout);
Assert.Equal(expectedState, currentState.State);
Assert.Equal(expectedProgress, currentState.Progress);
}
[Fact]
public async Task Work_fails_with_info()
{
var expectedState = "41";
var expectedErrorText = "42";
var minWorkTime = TimeSpan.FromMilliseconds(10);
async Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
{
await Task.Delay(minWorkTime);
callback(expectedState, 0);
throw new Exception(expectedErrorText);
}
var work = Work.CreateByDelegate("", workAction);
//act
var begin = DateTime.Now;
await work.Start(provider, CancellationToken.None);
//assert
Assert.Equal(0, work.CountComplete);
Assert.Equal(1, work.CountStart);
Assert.Equal(1, work.CountErrors);
Assert.Null(work.CurrentState);
Assert.Null(work.LastComplete);
var error = work.LastError;
Assert.NotNull(error);
Assert.InRange(error.Start, begin, DateTime.Now);
Assert.InRange(error.End, begin, DateTime.Now);
Assert.InRange(error.ExecutionTime, minWorkTime, DateTime.Now - begin);
Assert.Contains(expectedErrorText, error.ErrorText, StringComparison.InvariantCultureIgnoreCase);
Assert.Equal(expectedState, error.State);
}
[Fact]
public async Task Stop()
{
var workTime = TimeSpan.FromMilliseconds(1_000);
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
=> Task.Delay(workTime, token);
var work = Work.CreateByDelegate("", workAction);
//act
var begin = DateTime.Now;
_ = work.Start(provider, CancellationToken.None);
await Task.Delay(10);
work.Stop();
await Task.Delay(10);
//assert
Assert.Equal(0, work.CountComplete);
Assert.Equal(1, work.CountStart);
Assert.Equal(0, work.CountErrors);
Assert.Null(work.LastComplete);
Assert.Null(work.LastError);
}
}
}

View File

@ -0,0 +1,119 @@
using System;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudInfrastructure.Background;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using Xunit;
namespace AsbCloudWebApi.Tests.UnitTests.Background;
public class BackgroundWorkerTest
{
private readonly IServiceProvider serviceProviderMock = Substitute.For<IServiceProvider, ISupportRequiredService>();
private readonly IServiceScope serviceScopeMock = Substitute.For<IServiceScope>();
private readonly IServiceScopeFactory serviceScopeFactoryMock = Substitute.For<IServiceScopeFactory>();
private readonly BackgroundWorker backgroundWorker;
public BackgroundWorkerTest()
{
serviceScopeFactoryMock.CreateScope().Returns(serviceScopeMock);
((ISupportRequiredService)serviceProviderMock).GetRequiredService(typeof(IServiceScopeFactory)).Returns(serviceScopeFactoryMock);
backgroundWorker = new BackgroundWorker(serviceProviderMock);
typeof(BackgroundWorker)
.GetField("minDelay", BindingFlags.NonPublic | BindingFlags.Instance)
?.SetValue(backgroundWorker, TimeSpan.FromMilliseconds(1));
}
[Fact]
public async Task Enqueue_ShouldReturn_WorkCount()
{
//arrange
const int workCount = 10;
var result = 0;
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
{
result++;
return Task.Delay(1);
}
//act
for (int i = 0; i < workCount; i++)
{
var work = Work.CreateByDelegate(i.ToString(), workAction);
backgroundWorker.Enqueue(work);
}
await backgroundWorker.ExecuteTask;
//assert
Assert.Equal(workCount, result);
}
[Fact]
public async Task Enqueue_Continues_AfterExceptions()
{
//arrange
const int expectadResult = 42;
var result = 0;
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
{
result = expectadResult;
return Task.CompletedTask;
}
var goodWork = Work.CreateByDelegate("", workAction);
Task failAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
=> throw new Exception();
var badWork = Work.CreateByDelegate("", failAction);
badWork.OnErrorAsync = (id, exception, token) => throw new Exception();
//act
backgroundWorker.Enqueue(badWork);
backgroundWorker.Enqueue(goodWork);
await backgroundWorker.ExecuteTask;
//assert
Assert.Equal(expectadResult, result);
Assert.Equal(1, backgroundWorker.Felled.Count);
Assert.Equal(1, backgroundWorker.Done.Count);
}
[Fact]
public async Task TryRemoveFromQueue_ShouldReturn_True()
{
//arrange
const int workCount = 5;
var result = 0;
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
{
result++;
return Task.Delay(10);
}
//act
for (int i = 0; i < workCount; i++)
{
var work = Work.CreateByDelegate(i.ToString(), workAction);
backgroundWorker.Enqueue(work);
}
var removed = backgroundWorker.TryRemoveFromQueue((workCount - 1).ToString());
await backgroundWorker.ExecuteTask;
//assert
Assert.True(removed);
Assert.Equal(workCount - 1, result);
Assert.Equal(workCount - 1, backgroundWorker.Done.Count);
}
}

View File

@ -1,16 +1,16 @@
using AsbCloudInfrastructure.Background;
using DocumentFormat.OpenXml.Drawing.Charts;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using System;
using System;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudInfrastructure.Background;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using Xunit;
namespace AsbCloudWebApi.Tests.Services;
namespace AsbCloudWebApi.Tests.UnitTests.Background;
//TODO: нужно поправить тесты, иногда они не проходят
public class PeriodicBackgroundWorkerTest
{
private IServiceProvider provider;
@ -35,10 +35,10 @@ public class PeriodicBackgroundWorkerTest
}
[Fact]
public async Task WorkRunsTwice()
public async Task WorkRunsTwice_ShouldReturn_WorkCount()
{
var workCount = 2;
var periodMs = 100d;
const int workCount = 2;
const double periodMs = 100d;
var period = TimeSpan.FromMilliseconds(periodMs);
@ -64,7 +64,7 @@ public class PeriodicBackgroundWorkerTest
[Fact]
public async Task Enqueue_continues_after_exceptions()
public async Task Enqueue_Continues_AfterExceptions()
{
var expectadResult = 42;
var result = 0;

View File

@ -0,0 +1,152 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudInfrastructure.Background;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using Xunit;
namespace AsbCloudWebApi.Tests.UnitTests.Background;
public class WorkTest
{
private readonly IServiceProvider serviceProviderMock = Substitute.For<IServiceProvider, ISupportRequiredService>();
private readonly IServiceScope serviceScopeMock = Substitute.For<IServiceScope>();
private readonly IServiceScopeFactory serviceScopeFactoryMock = Substitute.For<IServiceScopeFactory>();
public WorkTest()
{
serviceScopeFactoryMock.CreateScope().Returns(serviceScopeMock);
((ISupportRequiredService)serviceProviderMock).GetRequiredService(typeof(IServiceScopeFactory)).Returns(serviceScopeFactoryMock);
}
[Fact]
public async Task Start_ShouldReturn_Success()
{
//arrange
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
=> Task.CompletedTask;
var work = Work.CreateByDelegate("", workAction);
//act
var begin = DateTime.Now;
await work.Start(serviceProviderMock, CancellationToken.None);
var done = DateTime.Now;
var executionTime = done - begin;
//assert
Assert.Equal(1, work.CountComplete);
Assert.Equal(1, work.CountStart);
Assert.Equal(0, work.CountErrors);
Assert.Null(work.CurrentState);
Assert.Null(work.LastError);
var lastState = work.LastComplete;
Assert.NotNull(lastState);
Assert.InRange(lastState.Start, begin, done - 0.5 * executionTime);
Assert.InRange(lastState.End, done - 0.5 * executionTime, done);
Assert.InRange(lastState.ExecutionTime, TimeSpan.Zero, executionTime);
}
[Fact]
public async Task ExecutionWork_Invokes_Callback()
{
//arrange
const string expectedState = "42";
const double expectedProgress = 42d;
var timeout = TimeSpan.FromMilliseconds(40);
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
{
callback.Invoke(expectedState, expectedProgress);
return Task.Delay(timeout);
}
var work = Work.CreateByDelegate("", workAction);
//act
var begin = DateTime.Now;
_ = work.Start(serviceProviderMock, CancellationToken.None);
await Task.Delay(timeout / 3);
//assert
Assert.Equal(0, work.CountComplete);
Assert.Equal(1, work.CountStart);
Assert.Equal(0, work.CountErrors);
Assert.NotNull(work.CurrentState);
Assert.Null(work.LastComplete);
Assert.Null(work.LastError);
var currentState = work.CurrentState;
Assert.NotNull(currentState);
Assert.InRange(currentState.Start, begin, begin + timeout);
Assert.InRange(currentState.StateUpdate, begin, begin + timeout);
Assert.Equal(expectedState, currentState.State);
Assert.Equal(expectedProgress, currentState.Progress);
}
[Fact]
public async Task ExecutionWork_ShouldReturn_FailsWithInfo()
{
//arrange
const string expectedState = "41";
const string expectedErrorText = "42";
var minWorkTime = TimeSpan.FromMilliseconds(10);
async Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
{
await Task.Delay(minWorkTime);
callback(expectedState, 0);
throw new Exception(expectedErrorText);
}
var work = Work.CreateByDelegate("", workAction);
//act
var begin = DateTime.Now;
await work.Start(serviceProviderMock, CancellationToken.None);
//assert
Assert.Equal(0, work.CountComplete);
Assert.Equal(1, work.CountStart);
Assert.Equal(1, work.CountErrors);
Assert.Null(work.CurrentState);
Assert.Null(work.LastComplete);
var error = work.LastError;
Assert.NotNull(error);
Assert.InRange(error.Start, begin, DateTime.Now);
Assert.InRange(error.End, begin, DateTime.Now);
Assert.InRange(error.ExecutionTime, minWorkTime, DateTime.Now - begin);
Assert.Contains(expectedErrorText, error.ErrorText, StringComparison.InvariantCultureIgnoreCase);
Assert.Equal(expectedState, error.State);
}
[Fact]
public async Task Stop_ShouldReturn_Success()
{
//arrange
var workTime = TimeSpan.FromMilliseconds(1_000);
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
=> Task.Delay(workTime, token);
var work = Work.CreateByDelegate("", workAction);
//act
var begin = DateTime.Now;
_ = work.Start(serviceProviderMock, CancellationToken.None);
await Task.Delay(10);
work.Stop();
await Task.Delay(10);
//assert
Assert.Equal(0, work.CountComplete);
Assert.Equal(1, work.CountStart);
Assert.Equal(0, work.CountErrors);
Assert.Null(work.LastComplete);
Assert.Null(work.LastError);
}
}