What is a good practice to write unit-test on .net core Ihostedservice? - unit-testing

I have a background task initiated in .net core 2.0 startup, inherits from backgroundservice, implementing StartAsync, StopAsync and ExecuteAsync. This task is to update some data in database table periodically based on some business logic.
While I can run the backgroundtask as an application and test using logs, db check and with the help of other tools, can the unit-testing is necessary for testing the backgroundtask? If so how to register the task as a service with dependencies and trigger the start and stop methods to assert the actual vs expected? Appreciate some basic sample unit-test method on testing timer based .net core ihostedservice backgroundtask.
Here is my basic test start just for sample, but not completed yet. Having said that, this is just a thought but not the exact working test. Here is what need some help from the community. Can also add some more asserts i.e. Assert.Verify()?
[Fact]
public async void Run_background_task_success()
{
//Arrange
IServiceCollection services = new ServiceCollection();
services.AddHostedService<BackgroundManagerTask>();
var serviceProvider = services.BuildServiceProvider();
var service = serviceProvider.GetService<IHostedService>() as BackgroundManagerTask;
var isExecuted = false;
if(await service.StartAsync(CancellationToken.None))
{
isExecuted = true;
}
await Task.Delay(10000);
Assert.True(isExecuted);
await service.StopAsync(CancellationToken.None);
}

Here's how I usually do it. You mention you are going to the database to update some data, so I'm assuming you are expecting that as a dependency from BackgroundManager
[Fact]
public void BackgroundManagerUpdatingDataTest()
{
// Arrange
Mock<IDataAccess> dbMock = new Mock<IDataAccess>();
dbMock.Setup(x => x.UpdateSomethingInDB(It.IsAny<BusinessObject>())).Returns(1); // One row updated from the DML in UpdateSomethingInDB from the BusinessObject
BackgroundManager sut = new BackgroundManager(dbMock.Object); // System under test.
// Act
await sut.StartAsync(CancellationToken.None);
await Task.Delay(500); // Give the test some time to execute.
await sut.StopAsync(CancellationToken.None); // Stop the Background Service.
// Assert
dbMock.Verify(x => x.UpdateSomethingInDB(It.IsAny<BusinessObject>()), Times.Exactly(1));
}
Above, we are plainly testing the update to the database occurred by Mocking the data access call and verifying that it was called exactly once.
You could of course Mock any other dependency out using Moq and Assert on anything else you want to verify.

Related

How best to unit test a ServiceStack service that uses IServiceGateway to call other internal services

I've been following the guidelines here - https://docs.servicestack.net/testing
I'm trying to do unit testing rather than integration, just to cut down the level of mocking and other complexities.
Some of my services call some of my other services, via the recommended IServiceGateway API, e.g. Gateway.Send(MyRequest).
However when running tests i'm getting System.NotImplementedException: 'Unable to resolve service 'GetMyContentRequest''.
I've used container.RegisterAutoWired() which is the service that handles this request.
I'm not sure where to go next. I really don't want to have to start again setting up an integration test pattern.
You're likely going to continually run into issues if you try to execute Service Integrations as unit tests instead of Integration tests which would start in a verified valid state.
But for Gateway Requests, they're executed using an IServiceGateway which you can choose to override by implementing GetServiceGateway() in your custom AppHost with a custom implementation, or by registering an IServiceGatewayFactory or IServiceGateway in your IOC, here's the default implementation:
public virtual IServiceGateway GetServiceGateway(IRequest req)
{
if (req == null)
throw new ArgumentNullException(nameof(req));
var factory = Container.TryResolve<IServiceGatewayFactory>();
return factory != null ? factory.GetServiceGateway(req)
: Container.TryResolve<IServiceGateway>()
?? new InProcessServiceGateway(req);
}
Based on the discussion in the answer by #mythz, this is my solution:
Use case like OP: Test the "main" service, and mock the "sub service", and like OP I'd want to do that with Unit test (so BasicAppHost), because it's quicker, and I believe it is easier to mock services that way (side note: for AppHost based integration test, SS will scan assemblies for (real) Services, so how to mock? Unregister from "container" and replace w. mock?).
Anyway, for the unit test:
My "main" service is using another service, via the IServiceGateway (which is the officially recommended way):
public MainDtoResponse Get(MainDto request) {
// do some stuff
var subResponse = Gateway.Send(new SubDto { /* params */ });
// do some stuff with subResponse
}
In my test setup:
appHost = new BasicAppHost().Init();
var mockGateway = new Mock<IServiceGateway>(); // using Moq
mockGateway.Setup(x => x.Send<SubDtoResponse>(It.IsAny<SubDto>()))
.Returns(new SubDtoResponse { /* ... */ });
container.Register(mockGateway.Object);
So the IServiceGateway must be mocked, and then the Send method is the important one. What I was doing wrong was to mock the service, when I should have mocked the Gateway.
Then call the main service (under test) in the normal fashion for a Unit Test, like in the docs:
var s = appHost.Container.Resolve<MainService>(); // must be populated in DI manually earlier in code
s.Get(new MainDto { /* ... */ })
PS: The mockGateway.Setup can be used inside each test, not necessarily in the OneTimeSetUp.

Test runners inconsistent with HttpClient and Mocking HttpMessageRequest XUnit

So let me start by saying I've seen all the threads over the wars between creating a wrapper vs mocking the HttpMethodRequest. In the past, I've done the wrapper method with great success, but I thought I'd go down the path of Mocking the HttpMessageRequest.
For starters here is an example of the debate: Mocking HttpClient in unit tests. I want to add that's not what this is about.
What I've found is that I have tests upon tests that inject an HttpClient. I've been doing a lot of serverless aws lambdas, and the basic flow is like so:
//some pseudo code
public class Functions
{
public Functions(HttpClient client)
{
_httpClient = client;
}
public async Task<APIGatewayResponse> GetData(ApiGatewayRequest request, ILambdaContext context)
{
var result = await _client.Get("http://example.com");
return new APIGatewayResponse
{
StatusCode = result.StatusCode,
Body = await result.Content.ReadStringAsAsync()
};
}
}
...
[Fact]
public void ShouldDoCall()
{
var requestUri = new Uri("http://example.com");
var mockResponse = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(expectedResponse) };
var mockHandler = new Mock<HttpClientHandler>();
mockHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
It.IsAny<HttpRequestMessage>(),
It.IsAny<CancellationToken>())
.ReturnsAsync(mockResponse);
var f = new Functions(new HttpClient(handler.Object);
var result = f.GetData().Result;
handlerMock.Protected().Verify(
"SendAsync",
Times.Exactly(1), // we expected a single external request
ItExpr.Is<HttpRequestMessage>(req =>
req.Method == HttpMethod.Get &&
req.RequestUri == expectedUri // to this uri
),
ItExpr.IsAny<CancellationToken>()
);
Assert.Equal(200, result.StatusCode);
}
So here's where I have the problem!
When all my tests run in NCrunch they pass, and pass fast!
When I run them all manually with Resharper 2018, they fail.
Equally, when they get run within the CI/CD platform, which is a docker container with the net core 2.1 SDK on a Linux distro, they too fail.
These tests should not be run in parallel (read the tests default this way). I have about 30 tests around these methods combined, and each one randomly fails on the moq verify portion. Sometimes they pass, sometimes they fail. If I break down the tests per test class and on run the groups that way, instead of all in one, then these will all pass in chunks. I'll also add that I have even gone through trying to isolate the variables per test method to make sure there is no overlap.
So, I'm really lost with trying to handle this through here and make sure this is testable.
Are there different ways to approach the HttpClient where it can consistently pass?
After lots of back n forth. I found two of situations from this.
I couldn't get parallel processing disabled within the docker setup, which is where I thought the issue was (I even made it do thread sleep between tests to slow it down (It felt really icky to me)
I found that all the tests l locally ran through the test runners were telling me they passed when about 1/2 failed on the docker test runner. What ended up being the issue was a magic string area when seeing and getting environment variables.
Small caveat to call out, Amazon updated their .NET Core lambda tools to install via dotnet cli, so this was updated in our docker image.

How can I unit test a MassTransit consumer that builds and executes a routing slip?

In .NET Core 2.0 I have a fairly simple MassTransit routing slip that contains 2 activities. This is built and executed in a consumer and it all ties back to an automatonymous state machine. It all works great albeit with a few final clean tweaks needed.
However, I can't quite figure out the best way to write unit tests for my consumer as it builds a routing slip. I have the following code in my consumer:
public async Task Consumer(ConsumerContext<ProcessRequest> context)
{
var builder = new RoutingSlipBuilder(NewId.NextGuid());
SetupRoutingSlipActivities(builder, context);
var routingSlip = builder.Build();
await context.Execute(routingSlip).ConfigureAwait(false);
}
I created the SetupRoutingSlipActivities method as I thought it would help me write tests to make sure the right activities were being added and it simply looks like:
public void SetupRoutingSlipActivities(RoutingSlipBuilder builder, ConsumeContext<IProcessCreateLinkRequest> context)
{
builder.AddActivity(
nameof(ActivityOne),
new Uri("execute_activity_one_example_address"),
new ActivityOneArguments(
context.Message.Id,
context.Message.Name)
);
builder.AddActivity(
nameof(ActivityTwo),
new Uri("execute_activity_two_example_address"),
new ActivityTwoArguments(
context.Message.AnotherId,
context.Message.FileName)
);
}
I tried to just write tests for the SetupRoutingSlipActivities by using a Moq mock builder and a MassTransit InMemoryTestHarness but I found that the AddActivity method is not virtual so I can't verify it as such:
aRoutingSlipBuilder.Verify(x => x.AddActivity(
nameof(ActivityOne),
new Uri("execute_activity_one_example_address"),
It.Is<ActivityOne>(y => y.Id == 1 && y.Name == "A test name")));
Please ignore some of the weird data in the code examples as I just put up a simplified version.
Does anyone have any recommendations on how to do this? I also wanted to test to make sure the RoutingSlipBuilder was created but as that instance is created in the Consume method I wasn't sure how to do it! I've searched a lot online and through the MassTransit repo but nothing stood out.
Look at how the Courier tests are written, there are a number of test fixtures available to test routing slip activities. While they aren't well documented, the unit tests are a working testament to how the testing is used.
https://github.com/MassTransit/MassTransit/blob/develop/src/MassTransit.Tests/Courier/TwoActivityEvent_Specs.cs

Wrapper around TASKs in C#

I am using tasks in WinForms (.NET 4.0) to perform lengthy operations like WCF call. Application is already in product with heavy use of Tasks (almost all the methods which uses Tasks are void).
During the unit testing we have used AutoResetEvents (in actual code) to find out when the given task is completed then perform assert.
This gives me a thought that almost all the AutoResetEvent are waste of effort. They are just fulfilling unit testing needs, nothing else.
Can we create a wrapper around Tasks likewise when actual code run... they should work in background and in case of unit testing they should be synchronous.
Similar to below link for BackgroundWorker.
http://si-w.co.uk/blog/2009/09/11/unit-testing-code-that-uses-a-backgroundworker/
Why can't you simply use the continuation for tasks in your wrapper, like this:
var task = ...
task.ContinueWith(t => check task results here)
Also, unit tests can be marked as async, if they have a return type Task, so you can use an await there, and after that do your asserts:
[Test]
public async Task SynchronizeTestWithRecurringOperationViaAwait()
{
var sut = new SystemUnderTest();
// Execute code to set up timer with 1 sec delay and interval.
var firstNotification = sut.StartRecurring();
// Wait that operation has finished two times.
var secondNotification = await firstNotification.GetNext();
await secondNotification.GetNext();
// Assert outcome.
Assert.AreEqual("Init Poll Poll", sut.Message);
}
Another approach (from the same article) is to use a custom task scheduler, which will be synchronous in case of unit testing:
[Test]
public void TestCodeSynchronously()
{
var dts = new DeterministicTaskScheduler();
var sut = new SystemUnderTest(dts);
// Execute code to schedule first operation and return immediately.
sut.StartAsynchronousOperation();
// Execute all operations on the current thread.
dts.RunTasksUntilIdle();
// Assert outcome of the two operations.
Assert.AreEqual("Init Work1 Work2", sut.Message);
}
Same MSDN magazine contains nice article about best practices for async unit testing. Also async void should be used only as an event handler, all other methods should have async Task signature.

How to unit test a synchronous method calling asynchronous method?

What is the correct way to write a unit test for a synchronous method calling async methods.
Right now my unit test are passing, but when I try to open the page, it never returns.
Why isn't my unit test failing? How can I make it fail?
I replicated my problem with this simple code:
My passing test:
[TestMethod]
public void DoSomeWork_WhenWeDoSomeWork_ShouldReturnDone()
{
var service = new SyncService();
const string expected = "Done";
var actual = service.DoSomeWork();
Assert.AreEqual(expected, actual);
}
My view that never returns:
public ActionResult Index()
{
var syncService = new SyncService();
return View((object)syncService.DoSomeWork());
}
My service that never returns to view:
public class SyncService
{
public string DoSomeWork()
{
return SomeWork().GetAwaiter().GetResult();
}
private async Task<string> SomeWork()
{
var task1 = Task.Delay(1000);
var task2 = Task.Delay(1000);
await Task.WhenAll(task1, task2);
return "Done";
}
}
I don't think I can help you with this specific example, but I think a good general strategy is to write two tests. One to test if the synchronous method passes the correct data and an other to test if the asynchronous method works properly.
I mostly work in JavaScript and that general approach works for me. Also you can check the documentation of your testing frameworks, maybe it provides some methods for this.
First, don't block on async code (link to my blog). By blocking on async code, you're actually causing a deadlock. This deadlock does not happen in your unit test because unit tests run in a thread pool context, not an ASP.NET context (link to my blog).
There are good reasons for not having synchronous wrappers for asynchronous methods. So I recommend getting rid of DoSomeWork completely, leaving only SomeWork (renamed to SomeWorkAsync).
To solve your problem, you should use asynchronous controller actions.