Unit Testing and Mocking SubscriberClient (Google Pub/Sub) in a C# Project - c#

I'm using Google Cloud Pub/Sub (Google.Cloud.PubSub.V1 (2.2.0)) in a .NET Core 3.1 project.
I'm trying to write a unit test for the code that retrieves messages from a Google Pub/Sub subscription.
My code is similar to what you find in the Google documentation.
var subscriber = await SubscriberClient.CreateAsync(subscriptionName);
try
{
var startTask = subscriber.StartAsync(async (PubsubMessage message, CancellationToken cancel) =>
{
//code
});
await Task.Delay(5000);
await subscriber.StopAsync(CancellationToken.None);
await startTask;
}
catch (Exception ex)
{
//code
}
Is there a way to mock SubscriberClient in a unit test? SubscriberClient doesn't appear to have an interface.
My unit tests are using NUnit (3.12.0) and Moq (4.14.5).
Any ideas would be appreciated.

Calling SubscriberClient.Create(...) instead of .CreateAsync(...) allows you to pass in the underlying SubscriberServiceApiClient instance(s).
Edit: Apologies, the above is incorrect, it should be:
Instantiate a SubscriberClientImpl directly instead of calling SubscriberClient.CreateAsync(...). This allows you to pass in the underlying SubscriberServiceApiClient instance(s).
Note that you can pass new SubscriberClient.Settings() for the settings argument, and null for the shutdown argument as defaults.
SubscriberServiceApiClient can be mocked - either directly, or by instantiated a SubscriberServiceApiClientImpl instance and passing in a mocked Subscriber.SubscriberClient.
When testing, note that SubscriberClient is multi-threaded and with default settings will call the callback passed to StartAsync concurrently from multiple threads.

Related

Im not able to mock ServiceBusReceivedMessage and ServiceBusMessageActions

we want to write unit-test for servicebus message trigger. we are using Azure.Messaging.ServiceBus nuget package
[FunctionName("serviebustrigger")]
public async Task Run ([ServiceBusTrigger("xxxxtopic", "xxxxsubscription", Connection = "Abs-Connection", AutoCompleteMessages = false)] ServiceBusReceivedMessage message, ServiceBusMessageActions messageActions)
{
_logger.LogInformation($"{nameof(Run)} execution started for MessageId:{{MessageId}}", message.MessageId);
try
{
//some code
await messageActions.CompleteMessageAsync(message);
}
catch (Exception ex)
{
await messageActions.DeadLetterMessageAsync(message);
}
}
Now I want to write unit test for the above code. But I'm not able mock ServiceBusReceivedMessage and ServiceBusMessageActions as these have Internal Constructor. Can someone suggest me better way to write unit test
There was an oversight with the implementation of ServiceBusMessageActions where the mocking constructor was initially missed. This was corrected in v5.2.0 of the extensions package.
With that fix, a parameterless constructor is available to ensure that ServiceBusMessageActions is usable with a mocking framework such as Moq or FakeItEasy. Each of the public members are virtual or settable and the class is not sealed. You should be able to mock using the same approach that you prefer for other types - whether with a mocking framework or inheriting from the class and creating your own mock type - and make use of the model factory to simulate behavior.
For ServiceBusReceivedMessage and other model types that are returned by service operations, the ServiceBusModelFactory is used to create instances for testing purposes.

How to write a test with xUnit in .NET Core using Dapper.Contrib?

I used Dapper.Contrib in my Asp.Net Core Web API project.
I encountered a problem while writing a test with xUnit in this project.
For example, here is my method that adds records to my data layer.
public async Task<bool> AddAsync(User entity)
{
await using var connection = _dbConnection.CreateDbConnection();
await connection.OpenAsync();
return await connection.InsertAsync(entity) > 0;
}
My xUnit method that I try to write according to this method is below.
[Fact]
public void AddAsync_Should_Return_As_Expected()
{
var connection = new Mock<DbConnection>();
//Arrange
_userDbConnection.Setup(u => u.CreateDbConnection()).Returns(connection.Object);
//Act
var result = _sut.AddAsync(_user).GetAwaiter().GetResult();
//Assert
//Assert.Equal(result,actual);
}
When I run this test method, I get object not set error in
'return await connection.InsertAsync(entity) > 0;' line.
What exactly is my fault?
I believe what is happening is you are using a mocked connection and trying to call a Dapper method, InsertAsync, on it. The Dapper method is inevitably failing because it is not a real connection.
I'm not sure how much value you get using mocks here. What you really want to know, when testing AddAsync, is does it actually do what you want it to do which is insert data into the database. So if I were you I would turn this test into an integration test by using a real connection instead of a mocked one. One way of doing this is to
Use a test database for the purposes of testing
Before running the test delete all data from the test database
In your assert use Dapper or otherwise to check that a query for the entity brings back the data you expect to be in the database.
I don't necessarily recommend this but another approach could be to use an in memory database. See for example unit testing Dapper Repositories.

Mocking IFlurl library methods using NSubstitute is throwing null reference exception

I am using flurl and I am trying to unit test the code below:
public class MyRestClient
{
public async Task<T> Request<T>(IFlurlRequest flurlRequest)
{
try
{
return await flurlRequest
.WithOAuthBearerToken("my-hardcoded-token")
.GetAsync()
.ReceiveJson<T>();
}
catch(HttpFlurlException)
{
throw new MyCustomException();
}
}
}
What I want to test is that if flurlRequest throws an exception of type HttpFlurlException then it will throw MyCustomException. My idea is to moq the flurlrequest and throw an exception. This is how I layed out my test:
var moq = Substitute.For<IFlurlRequest>();
// Problem is with line below:
moq.When(x => x.WithOAuthBearerToken("dummy")).Do(x => { throw new HttpFlurlException(); } );
var myClient = new MyRestClient();
Func<Task> call = async () => { await myClient.Request<object>(moq); };
// FluentAssertions
call.Should().Throw<MyCustomException>();
The code when ran returns a NullReferenceException:
Exception has occurred: CLR/System.NullReferenceException
An exception of type 'System.NullReferenceException' occurred in
Flurl.Http.dll but was not handled in user code: 'Object reference not
set to an instance of an object.'
at Flurl.Http.HeaderExtensions.WithHeader[T](T clientOrRequest, String name, Object value)
So I see its something related to headers... so I tried also mocking that by adding:
var moq = Substitute.For<IFlurlRequest>();
moq.Headers.Returns(new Dictionary<string, object> { {"dummy", new {} };
But I'm constantly getting the same exception. What am I doing wrong?
WithOAuthBearerToken is an extension method, which means it cannot be mocked directly by NSubstitute. When you call When..Do or Returns on an extension method it will run the real code of the extension method. (I recommend adding NSubstitute.Analyzers to your test project to detect these cases.)
Tracing through the extension method implementation at the time of writing, it should be possible to mock the Headers property to throw the required exception, but I think this is dragging in much too much internal knowledge of the library and will result in brittle tests that are tightly coupled to that specific implementation (which is what we are aiming to avoid with mocking!).
I would be very wary of mocking out a third part library in this way, as I outlined in this answer:
The other option is to test this at a different level. I think the friction in testing the current code is that we are trying to substitute for details of [a third-party library], rather than interfaces we've created for partitioning the logical details of our app. Search for "don't mock types you don't own" for more information on why this can be a problem (I've written about it before here).
If possible I suggest trying to use Flurl's built-in testing support instead. That should enable you to fake out the behaviour you need without requiring specific details about Flurl's internal implementation.

Await in Unit tests and EntityFramework stored in CallContext.SetData

I have MVC web app that uses EntityFramework context and it stores it in HttpContext.Current.Items. When HttpContext.Current isn't available then it uses CallContext.SetData to store data in current thread storage. HttpContext is used for web app itself and CallContext is used in unit tests to store the same EF DbContext there.
We are also trying to use async\await as we have library that relays a lot on them, and it works great in web app. But it fails in unit tests as CallContext.SetData isn't restored after thread returns to await block.
Here is simplified sample of the issue:
public async Task Test()
{
ContextUtils.DbContext = new SomeDbContext();
using (ContextUtils.DbContext){
await DoSomeActions();
}
}
public async Task DoSomeActions(){
var data = await new HttpClient().GetAsync(somePage);
// on next line code would fail as ContextUtils.DbContext is null
// as it wasn't synced to new thread that took it
var dbData = ContextUtils.DbContext.SomeTable.First(...);
}
So in that example ContextUtils.DbContext basically sets HttpContext\CallContext.SetData. And it works fine for web app, and fails in unit test as SetData isn't shared and on ContextUtils.DbContext.SomeTable.First(...); line DbContext is null.
I know that we can use CallContext.LogicalSetData\LogicalGetData and it would be shared withing ExecutionContext, but it requires item to be Serializable and i don't want to mark DbContext with serialization attribute as would try to serialize it.
I also saw Stephen's AsyncEx library (https://github.com/StephenCleary/AsyncEx) that has own SynchronizationContext, but it would require me to update my code and use AsyncContext.Run instead of Task.Run, and i'm trying to avoid code updating just for unit tests.
Is there any way to fix it without changing the code itself just to make it work for unit tests? And where EF DbContext should be stored in unit tests without passing it as parameter and to be able to use async\await?
Thanks
OK, there's a lot of things here.
Personally, I would look askance at the use of CallContext.GetData as a fallback to HttpContext.Current, especially since your code makes use of async. Consider using AsyncLocal<T> instead. However, it's possible that AsyncLocal<T> may also require serialization.
I also saw Stephen's AsyncEx library (https://github.com/StephenCleary/AsyncEx) that has own SynchronizationContext, but it would require me to update my code and use AsyncContext.Run instead of Task.Run, and i'm trying to avoid code updating just for unit tests.
A couple of things here:
You shouldn't be using Task.Run on ASP.NET in the first place.
Using Task.Run will prevent the (non-logical) call context from working, as well as HttpContext.Current. So I assume that your code is not accessing the DbContext from within the Task.Run code.
It sounds like your best option is to use my AsyncContext. This class was originally written for asynchronous unit tests (back before unit test frameworks supported asynchronous unit tests). You shouldn't need to update your code at all; just use it in your unit tests:
public void Test()
{
AsyncContext.Run(async () =>
{
ContextUtils.DbContext = new SomeDbContext();
using (ContextUtils.DbContext)
{
await DoSomeActions();
}
});
}
Avoid using async void. Make the test method await-able by used Task
[TestMethod]
public async Task Test() {
ContextUtils.DbContext = new SomeDbContext();
using (ContextUtils.DbContext) {
await DoSomeActions();
}
}
HttpContext is not available during unit test as it is tied to IIS which is not present during unit tests. Avoid tightly coupling your code to HttpContext treat it like a 3rd party resource and abstract it away behind code you can control. It will make testing maintaining and testing your code easier. Consider reviewing your current design.

How write a unit test for asynchronous Web API

I have a project in C# and Framework 6. I have a WEB API to call the methods.
what is the best way to write a unit test for async WebAPI methods?
This is the method I have:
[HttpGet]
[Route("GetYears")]
public Task<IEnumerable<Year>> GetYearsAsync()
{
return reditApplicationsRepository.GetYearsAsync();
}
You can use await to do this too.
[TestMethod]
public async Task GetYearsTest()
{
//_sut is the System Under Test, in this case is an instance of your controller
var years = await _sut.GetYears();
//Any Assert that you want
}
And to stub the return of the method creditApplicationsRepository.GetYearsAsync(); use this Task.FromResult(mockReturn).
The key objective of a unit test is to test the functionality of the code inside a method. So async or not should not be particularly relevant functionally.
So I would say do something like this:
[TestMethod]
public void GetYearsTest(){
//This would assume that your web api class has a mockable repository
var yourClass = new YourClass(new MockRepository());
//this is going to run your codes synchronously so you can get an immediate result to test on.
var years = yourClass.GetYears().Result;
//Run whatever test you want to on this
Assert.IsNotNull(years);
}
If the purpose of running an Async test is to determine if your code is thread safe you will have to get creative to determine how to properly test your code to ensure it is in fact safe. In order for me to assist with that I would need to know more about the repo that is being used here or what variables could potentially get thread locked or result in an error.

Categories