XUnit Mock not throwing exception like I would expect - c#

I have the following test that uses a mock of a class. When I try to throw an exception it never actually throws the exception. It goes on as if the method is actually being called and I am unsure why.
Here is the test:
[Fact]
public async Task ReadResultSetShouldRetry()
{
// Arrange
_cosmosUtilWrapper.Setup(x => x.ReadCosmosResultSet<CosmosRepositoryTests>(It.IsAny<FeedIterator>(), It.IsAny<ILogger>(), It.IsAny<CancellationToken>()))
.Throws(new Exception("It broke"));
var cosmosReadPolicy = new CosmosReadPolicy();
// Act
await Assert.ThrowsAsync<Exception>(async () => await CosmosRepository.ReadCosmosResultSetWithRetry<CosmosRepositoryTests>(_mockFeedIterator.Object, _logger, cosmosReadPolicy, CancellationToken.None));
// Assert
_cosmosUtilWrapper.Verify(x => x.ReadCosmosResultSet<CosmosRepositoryTests>(_mockFeedIterator.Object, _logger, default));
}
Here is the method that it is calling and I have it wrapped in a retry policy:
public static async Task<List<T>> ReadCosmosResultSetWithRetry<T>(
FeedIterator resultSet,
ILogger logger,
CosmosReadPolicy cosmosReadPolicy,
CancellationToken cancellationToken = default)
where T : class
{
CosmosUtilWrapper utilWrapper = new CosmosUtilWrapper();
var readCosmosResultSet = await cosmosReadPolicy.GetPolicy.ExecuteAsync(async () => await utilWrapper.ReadCosmosResultSet<T>(resultSet, logger, cancellationToken));
return readCosmosResultSet;
}
Here is the CosmosUtilWrapper and below is the actual Cosmos Util class:
public class CosmosUtilWrapper
{
public virtual async Task<List<T>> ReadCosmosResultSet<T>(
FeedIterator resultSet,
ILogger logger,
CancellationToken cancellationToken = default)
where T : class
{
return await CosmosUtil.ReadCosmosResultSet<T>(resultSet, logger, cancellationToken);
}
}
Here is the actual static Util method that is being returned in the above class. Had to go about it this way since this class is a static class and unit testing those are not very fun.
public static async Task<List<T>> ReadCosmosResultSet<T>(
FeedIterator resultSet,
ILogger logger,
CancellationToken cancellationToken = default)
where T : class
{
var foundDocuments = new List<T>();
while (resultSet.HasMoreResults)
{
using ResponseMessage responseMessage = await resultSet.ReadNextAsync(cancellationToken);
if (responseMessage.IsSuccessStatusCode)
{
using StreamReader streamReader = new StreamReader(responseMessage.Content);
using JsonTextReader textReader = new JsonTextReader(streamReader);
foundDocuments.AddRange((await JObject.LoadAsync(textReader, cancellationToken)).GetValue("Documents").ToObject<List<T>>());
}
else
{
throw new Exception($"Unable to read cosmos result set. Status code: {responseMessage.StatusCode}");
}
}
return foundDocuments;
}
Finally, Here is the message I get when running the tests
Message:
Assert.Throws() Failure
Expected: typeof(System.Exception)
Actual: (No exception was thrown)

It is because the mockup object _cosmosUtilWrapper in ReadResultSetShouldRetry() is never used in Task<List<T>> ReadCosmosResultSetWithRetry<T>.
In methode Task<List<T>> ReadCosmosResultSetWithRetry<T> you initialize a new object CosmosUtilWrapper utilWrapper = new CosmosUtilWrapper();. So, this object is not the same as the object above.
You can get the object from the mockup object with the following code: _cosmosUtilWrapper.Object. Pass this object in the function or in the constructor of the class when you remove the static from the methode.
For example:
public static async Task<List<T>> ReadCosmosResultSetWithRetry<T>(
FeedIterator resultSet,
ILogger logger,
CosmosReadPolicy cosmosReadPolicy,
CosmosUtilWrapper utilWrapper,
CancellationToken cancellationToken = default)
where T : class
{
var readCosmosResultSet = await cosmosReadPolicy.GetPolicy.ExecuteAsync(async () => await utilWrapper.ReadCosmosResultSet<T>(resultSet, logger, cancellationToken));
return readCosmosResultSet;
}
For example Test:
[Fact]
public async Task ReadResultSetShouldRetry()
{
// Arrange
_cosmosUtilWrapper.Setup(x => x.ReadCosmosResultSet<CosmosRepositoryTests>(It.IsAny<FeedIterator>(), It.IsAny<ILogger>(), It.IsAny<CancellationToken>()))
.Throws(new Exception("It broke"));
var cosmosReadPolicy = new CosmosReadPolicy();
var utilWrapper = _cosmosUtilWrapper.Object;
// Act
await Assert.ThrowsAsync<Exception>(async () => await CosmosRepository.ReadCosmosResultSetWithRetry<CosmosRepositoryTests>(_mockFeedIterator.Object, _logger, cosmosReadPolicy, utilWrapper, CancellationToken.None));
// Assert
_cosmosUtilWrapper.Verify(x => x.ReadCosmosResultSet<CosmosRepositoryTests>(_mockFeedIterator.Object, _logger, default));
}

Related

Unit test .net 6 console app using IHostApplicationLifeTime

I have a class (CronClass) that inherits from IHostedService with 2 methods which are StartAsync(CancellationToken) and StopAsync(CancellationToken).
How do you go about unit testing the StartAsync method to verify that the code was executed, using Moq Library?
For example:
public class CronClass: IHostedService
{
private readonly IHostedApplicationLifetime applicationLifetime;
private readonly IService service;
// IHostedApplicationLifetime/IService are injected DI to via the constructor
public Task StartAsync(CancellationToken cancellationToken)
{
applicationLifeTime.ApplicationStarted.Register(() =>
{
Task.Run(async () =>
{
log.LogInformation("Cron Started");
await service.Process();
});
});
}
//...
}
I would start with creating a mock of IHostApplicationLifetime
public class MockHostApplicationLifetime : IHostApplicationLifetime, IDisposable
{
internal readonly CancellationTokenSource _ctsStart = new CancellationTokenSource();
internal readonly CancellationTokenSource _ctsStopped = new CancellationTokenSource();
internal readonly CancellationTokenSource _ctsStopping = new CancellationTokenSource();
public MockHostApplicationLifetime()
{
}
public void Started()
{
_ctsStart.Cancel();
}
CancellationToken IHostApplicationLifetime.ApplicationStarted => _ctsStart.Token;
CancellationToken IHostApplicationLifetime.ApplicationStopping => _ctsStopping.Token;
CancellationToken IHostApplicationLifetime.ApplicationStopped => _ctsStopped.Token;
public void Dispose()
{
_ctsStopped.Cancel();
_ctsStart.Dispose();
_ctsStopped.Dispose();
_ctsStopping.Dispose();
}
public void StopApplication()
{
_ctsStopping.Cancel();
}
}
In your unit test create a mock of IService. Create instance of CronClass and call cronClass.StartAsync. Then start MockHostApplicationLifetime. It will trigger registered callback ApplicationStarted.Register. Then verify that Process() was called.
You are starting the task in Register method, so it can happen that the unit test can finish before the task is created and service.Process is called. In that case I would wait some time before verification.
[Test]
public async Task Test1()
{
var hal = new MockHostApplicationLifetime();
var mockService = new Mock<IService>();
var cronClass = new CronClass(hal, mockService.Object);
await cronClass.StartAsync(CancellationToken.None);
hal.Started();
// maybe not needed, test passed without the delay
//await Task.Delay(500);
mockService.Verify(mock => mock.Process());
}

How can I return Task<bool> from a Mock method using Xunit and Moq

I have this Repository class
public class ComplaintRepository : IComplaintRepository
{
private readonly IAppDbContext _context;
public ComplaintRepository(IAppDbContext context)
{
_context = context;
}
public async Task<bool> AddAsync(Complaint complaint, CancellationToken token)
{
await Task.Run(() => _context.Complaints.AddAsync(complaint));
return await SaveAsync(token);
}
private async Task<bool> SaveAsync(CancellationToken token)
{
if (await _context.SaveChangesAsync(token) > 0)
return true;
else
return false;
}
I want to set up a test for the class using xUnit and Moq. When I mock the behavior of the AddAsync method like so:
public class ComplaintRepositoryTests
{
[Fact]
public async void AddAsync_WithValidComplaint_ShouldReturnTrue()
{
// Arrange
var mockContext = new Mock<IAppDbContext>();
mockContext.Setup(x => x.Complaints.AddAsync(It.IsAny<Complaint>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(true));
mockContext.Setup(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(1);
var complaintRepository = new ComplaintRepository(mockContext.Object);
var complaint = new Complaint();
// Act
var result = await complaintRepository.AddAsync(complaint, CancellationToken.None);
// Assert
Assert.True(result);
mockContext.Verify(x => x.Complaints.AddAsync(It.IsAny<Complaint>(), It.IsAny<CancellationToken>()), Times.Once());
mockContext.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
}
}
I am getting an error for the return type of the AddAsync method.
this is the error I am getting:
cannot convert from 'System.Threading.Tasks.Task<bool>' to 'System.Threading.Tasks.ValueTask<Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<CCMP.Domain.Complaints.Complaint>>'
I don't know how to go about it. I have tried all the things I have seen online to no avail.

Unit test the code which is in Task.Run() in C#

I have to write unit test to verify the code which was run by Task.Run(),which is wrapped inside an async action as shown below.Since I am not awaiting for the task to complete I am not able to achieve this as Task.Run() runs separately.
The requirement is that The call this.KeepAlive(accountNumber, token) should not be waited to complete to call the next statement.But if that KeepAlive service call fails or any validation fails then it should be logged.
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
readonly IService service;
readonly IService2 service2;
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger, IService service, IService2 service2)
{
_logger = logger;
this.service = service;
this.service2 = service2;
}
[HttpGet]
public async Task<bool> Testmethod(string accountNumber, string viewName, CancellationToken token)
{
_ = Task.Run(() => this.KeepAlive(accountNumber, token));
var accountStatus = await this.service2.GetValidName(viewName, token);
return accountStatus=="Myname";
}
private async Task KeepAlive(string name, CancellationToken token)
{
if (string.IsNullOrEmpty(name))
{
_logger.LogError("name is empty or null");
return;
}
try
{
var isAlive = await this.service.ChekStatusAsyc(name, token);
if (!isAlive)
{
_logger.LogError("Unable to process the request");
}
}
catch
{
_logger.LogError("Service ChekStatusAsyc Failed");
}
}
}
}
I need to verify below in my unit tests
whether service was called
validation logging happened
Exception logging
The Test which was written as below will not work, since I am not awaiting for task to complete.
[Fact]
public async void KeepAliveAsyncShouldBeCalledErrorShouldBeLoggedIfServiceFails()
{
var mockLogger = new Mock<ILogger<WeatherForecastController>>();
var mockservice = new Mock<service>();
mockservice.Setup(x => x.ChekStatusAsyc(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(false);
var mockservice2 = new Mock<service2>();
var controller = new WeatherForecastController(mockLogger.Object, mockservice.Object, mockservice2.Object);
var result = await controller.Testmethod("account0", "test");
mockservice.Verify(x => x.ChekStatusAsyc(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
}
As I am not awaiting, I can't verify anything which was run by Task.Run().
If I use
await this.KeepAlive(accountNumber, token) then the test case which I wrote will work as expected,but it will wait for the task to complete.which is not as per the requirement.
Any suggestions?

C# UnitTest Async Method Error - System.MissingMethodException: Method not found:

Hi I have a window services project that I already successfully implement to the server. But I have to create unit test which I stuck for weeks to solve it. Can any of you guys help me?
I have clean the project several time and the issue come out when I try to setup the Mock based on my interface
I also have refactoring the code several time but fail to run the
unittest :(
Here is my code:
Interface
public interface IJobScheduler
{
Task<HttpResponseMessage> GetASync(HttpClient client, string destination, CancellationTokenSource cancelToken);
Task<bool> RunJobAgent(HttpClient client);
}
Class (purposely create to inject data using unit test)
public class JobSchedular
{
private IJobScheduler iJobScheduler;
public JobSchedular(IJobScheduler ijobscheduler) => iJobScheduler = ijobscheduler;
public JobSchedular() => iJobScheduler = new JobSchedularSvc();
public async Task<HttpResponseMessage> GetASync(HttpClient client, string destination, CancellationTokenSource cancelToken)
{
Task<HttpResponseMessage> result = iJobScheduler.GetASync(client, destination, cancelToken);
return await result;
}
}
Actual Class
public partial class JobSchedularSvc : ServiceBase, IJobScheduler
{
public async Task<HttpResponseMessage> GetASync(HttpClient client, string destination, CancellationTokenSource cancelToken)
{
try
{
HttpResponseMessage response;// = new HttpResponseMessage();
using (client)
{
response = await client.GetAsync(destination, cancelToken.Token);
}
return response;
}
catch (Exception ex)
{
LogHandler.InsertLog(LogLevel.Error, $"FAILED: GetAsync() - {ex.Message}", ex.StackTrace, "JobSchedulerSvc", "JobSchedulerSvc", null, null, null, null).Wait();
return null;
}
}
}
Test Method
public async Task Test()
{
var message = new HttpResponseMessage(HttpStatusCode.OK);
JobScheduler = new Mock<IJobScheduler>();
JobScheduler.Setup(test => test.GetASync(It.IsAny<HttpClient>(), It.IsAny<string>(), It.IsAny<CancellationTokenSource>()))
.Returns(Task.FromResult(new HttpResponseMessage() { StatusCode = HttpStatusCode.OK, Content = new StringContent("{'Result':true,'Exception':[]}") }));
CancellationTokenSource cancelToken = new CancellationTokenSource();
var response = new JobSchedular(JobScheduler.Object).GetASync(new HttpClient(), "/api/job/runjobagent", cancelToken);
var result = await response.Result.Content.ReadAsStringAsync();
Assert.AreEqual(result, "{'Result':true,'Exception':[]}");
}
I just want to call the local function GetAsync() which returns httpResponseMessage
I edit the .csproj file and add this line it works
<PropertyGroup>
  <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
  <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>

Getting result from Func<Task<T>>

Method Under Test
protected override async Task<Name> DoExecuteAsync(NameContext context)
{
context.ThrowIfNull("context");
var request = new Request
{
Id = context.Id,
Principal = context.UserPrincipal,
};
return await this.repository.NameAsync(request, new CancellationToken(), context.ControllerContext.CreateLoggingContext());
}
protected override Name HandleError(NameContext viewContext, Exception exception)
{
if (this.errorSignaller != null)
{
this.errorSignaller.SignalFromCurrentContext(exception);
}
return Name.Unknown;
}
This is implementation of
public abstract class BaseQueryAsync<TInput, TOutput> : IQueryAsync<TInput, TOutput>
{
public async Task<TOutput> ExecuteAsync(TInput context)
{
try
{
return await this.DoExecuteAsync(context);
}
catch (Exception e)
{
return this.HandleError(context, e);
}
}
protected abstract Task<TOutput> DoExecuteAsync(TInput context);
protected virtual TOutput HandleError(TInput viewContext, Exception exception)
{
ExceptionDispatchInfo.Capture(exception).Throw();
}
}
Test Case goes like below
[SetUp]
public void Setup()
{
var httpContext = MvcMockHelpers.MockHttpContext(isAuthenticated: true);
this.controller = new Mock<Controller>();
this.controller.Object.SetMockControllerContext(httpContext.Object);
this.repoMock = new Mock<IRepository>();
this.errorSignaller = new Mock<IErrorSignaller>();
this.query = new NameQuery(this.repoMock.Object, this.errorSignaller.Object);
this.userPrinciple = new Mock<IPrincipal>();
this.context = new NameContext(this.controller.Object.ControllerContext, this.userPrinciple.Object);
}
[Test]
public async Task TestDoExecuteAsyncWhenRepositoryFails()
{
// Arrange
this.repoMock.Setup(
x => x.NameAsync(
It.IsAny<Request>(),
It.IsAny<CancellationToken>(),
It.IsAny<ILoggingContext>())).Throws<Exception>();
// Act
Func<Task<Name>> act = async () => await this.query.ExecuteAsync(this.context);
// Assert
act.ShouldNotThrow();
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
}
To verify the Name Object ,When I use the var result = await act() before the line
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
The this.errorSignaller.Verify fails since it's count is 2 instead of 1. My intention is to check the Name object along with below code.
act.ShouldNotThrow();
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
I knew that if I write a new test case I can easily verify it, but is there any way I can do altogether in this test?
If you want to test the result then use:
Name result = await this.query.ExecuteAsync(this.context);
result.Should().Be(expectefResult);
Make sure to make your test method public async Task
Update
To be able to verify name you would need to set it in the function.
//...code removed for brevity
Name expectedName = Name.Unknown;
Name actualName = null;
// Act
Func<Task> act = async () => {
actualName = await this.query.ExecuteAsync(this.context);
};
// Assert
act.ShouldNotThrow();
actualName
.Should().NotBeNull()
.And.Be(expectedName);
//...rest of code
Original
As already mentioned in the comments, act is a function that returns a Task.
While its implementation is awaited, the function itself still needs to be invoked. And since the function returns a Task it too would need to be awaited.
Func<Task<Name>> act = async () => await this.query.ExecuteAsync(this.context);
var name = await act();
It is the same as having the following function.
async Task<Name> act() {
return await this.query.ExecuteAsync(this.context);
}
You would have to await it the same way
var name = await act();
The only difference being that the former example has the function in a delegate.
Try to avoid mixing blocking calls like .Result with async/await code. This tends to cause deadlocks.
You can try to check it with
await query.ExecuteAsync(this.context);
or
this.query.ExecuteAsync(this.context).GetAwaiter().GetResult();
and in case of Func:
act.Invoke().GetAwaiter().GetResult();

Categories

Resources