XUnit unit tests for MassTransit consumer - c#

I am using MassTransit 5.5.5 version and xunit 2.4.1
My consumer looks like this
public class StorageInformationConsumer : IConsumer<CreateStorageInformationSummary>
{
private readonly IBus _serviceBus;
private readonly USIntegrationQueueServiceContext _USIntegrationQueueServiceContext;
public StorageInformationConsumer(IBus serviceBus, USIntegrationQueueServiceContext USIntegrationQueueServiceContext)
{
_serviceBus = serviceBus;
_USIntegrationQueueServiceContext = USIntegrationQueueServiceContext;
}
public async Task Consume(ConsumeContext<CreateStorageInformationSummary> createStorageInformationSummarycontext)
{
//....
}
}
And my Test like this
public class StorageInformationConsumerTest
{
private readonly USIntegrationQueueServiceContext _dbContext;
private readonly Mock<IBus> _serviceBusMock;
private readonly StorageInformationConsumer _storageInformationConsumer;
public StorageInformationConsumerTest()
{
var options = new DbContextOptionsBuilder<USIntegrationQueueServiceContext>()
.UseInMemoryDatabase(databaseName: "InMemoryArticleDatabase")
.Options;
_dbContext = new USIntegrationQueueServiceContext(options);
_serviceBusMock = new Mock<IBus>();
_storageInformationConsumer = new StorageInformationConsumer(_serviceBusMock.Object, _dbContext);
}
[Fact]
public async void ItShouldCreateStorageInformation()
{
var createStorageInformationSummary = new CreateStorageInformationSummary
{
ClaimNumber = "C-1234",
WorkQueueItemId = 1,
StorageInformation = CreateStorageInformation(),
};
//How to consume
}
}
How to consume the CreateStorageInformationSummary message in order to call consumer, following doesn't work
var mockMessage = new Mock<ConsumeContext<CreateStorageInformationSummary>>(createStorageInformationSummary);
await _storageInformationConsumer.Consume(mockMessage.Object);

Since you have not clarified what is actually not working, the most I can provide is how to create the mock context and pass it to the subject method under test.
This is simple enough since ConsumeContext<T> is already an interface
[Fact]
public async Task ItShouldCreateStorageInformation() {
//Arrange
var createStorageInformationSummary = new CreateStorageInformationSummary {
ClaimNumber = "C-1234",
WorkQueueItemId = 1,
StorageInformation = CreateStorageInformation(),
};
//Mock the context
var context = Mock.Of<ConsumeContext<CreateStorageInformationSummary>>(_ =>
_.Message == createStorageInformationSummary);
//Act
await _storageInformationConsumer.Consume(context);
//Assert
//...assert the expected behavior
}
Also take note that the test has been updated to return async Task and not async void
Reference Moq Quickstart

Related

How to fix error in web api testing using xUnit

public class ControllerTest
{
private readonly IFixture fixure;
private readonly Mock<ICustomerService> service;;
private readonly CustController controller;
public ControllerTest()
{
service = new Mock<ICustomerService>();
controller = new CustController(service.Object);
fixure = new Fixture();
}
[Fact]
public async Task Controller _GetCustByIdName_ReturnOk()
{
//arrange
var customers = fixure.Create<IEnumerable<EmployeeDto>>();
var id = fixure.Create<string>();
var name = fixure.Create<string>();
service.Setup(x => x.GetCustomers(id,name)).ReturnsAsync(customers);
}
}
I have above code to unit test my method which accepts id and name and return list of employees. I am getting the error on last line of service.SetuUp as
isetup does not contain definition for returnsasync
Am I missing something?

Dependency Injection with XUnit Mediatr and IServiceCollection

Currently I'm able to handle IServiceCollection to inject mocks for particular services in the following manner.
public class TestClass
{
private IMediator _mediatr;
private void SetupProvider(IUnitOfWork unitOfWork, ILogger logger)
{
configuration = new ConfigurationBuilder().Build();
_services = new ServiceCollection();
_services.AddSingleton(configuration);
_services.AddScoped(x => unitOfWork);
_services.AddSingleton(logger);
_services.AddMediatR(Assembly.Load("Application"));
_services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggerBehaviour<,>));
_mediator = _services.BuildServiceProvider().GetService<IMediator>();
}
[Fact]
public async void UnitTest_Success()
{
var unitOfWork = new Mock<IUnitOfWork>();
var logger = new Mock<ILogger>();
SetupProvider(unitOfWork.Object, logger.Object);
var fixture = new Fixture();
var command = fixture.Create<MediatorCommand>();
unitOfWork.Setup(x => x.Repository.FindAll(It.IsAny<IList<long>>(), It.IsAny<bool?>()))
.ReturnsAsync(new List<Domain.Model>());
var response = await _mediatr.Send(command);
using (new AssertionScope())
{
response.Should().NotBeNull();
response.IsSuccess.Should().BeTrue();
}
}
}
For the following subject under test
public class MediatorCommand : IRequest<CommandResponse>
{
public string Name { get; set ;}
public string Address { get; set; }
}
public class MediatorCommandHandler : IRequestHandler<MediatorCommand, CommandResponse>
{
private readonly ILogger _logger;
private readonly IUnitOfWork _unitOfWork;
public MediatorCommandHandler(IUnitOfWork unitOfWork, ILogger logger)
{
_logger = logger;
_unitOfWork = unitOfWork;
}
public async Task<CommandResponse> Handle(MediatorCommand command, CancellationToken cancellationToken)
{
var result = new CommandResponse { IsSuccess = false };
try
{
var entity = GetEntityFromCommand(command);
await _unitOfWork.Save(entity);
result.IsSuccess = true;
}
catch(Exception ex)
{
_logger.LogError(ex, ex.Message);
}
return result;
}
}
This test runs fine and the unitOfWork and logger mocks are used in the command handlers.
I'm try to move this so that the IServiceCollection construction happens per class instead of each test using the following:
public class SetupFixture : IDisposable
{
public IServiceCollection _services;
public IMediator Mediator { get; private set; }
public Mock<IUnitOfWork> UnitOfWork { get; private set; }
public SetupFixtureBase()
{
UnitOfWork = new Mock<IUnitOfWork>();
configuration = new ConfigurationBuilder().Build();
_services = new ServiceCollection();
_services.AddSingleton(configuration);
_services.AddScoped(x => UnitOfWork);
_services.AddSingleton(new Mock<ILogger>().Object);
_services.AddMediatR(Assembly.Load("Application"));
_services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggerBehaviour<,>));
Mediator = _services.BuildServiceProvider().GetService<IMediator>();
}
public void Dispose()
{
Mediator = null;
_services.Clear();
_services = null;
}
}
public class TestClass : IClassFixture<SetupFixture>
{
protected readonly SetupFixture _setupFixture;
public UnitTestBase(SetupFixture setupFixture)
{
_setupFixture = setupFixture;
}
[Fact]
public async void UnitTest_Success()
{
var fixture = new Fixture();
var command = fixture.Create<MediatorCommand>();
_setupFixture.UnitOfWork.Setup(x => x.Repository.FindAll(It.IsAny<IList<long>>(), It.IsAny<bool?>()))
.ReturnsAsync(new List<Domain.Model>());
var response = await _mediatr.Send(command);
using (new AssertionScope())
{
response.Should().NotBeNull();
response.IsSuccess.Should().BeTrue();
}
}
}
Unfortunately with this method my mocks do not get injected on the command handler. Is there a way to get this to work?
Thank you,
I found the issue and it is not related to moving to IClassFixuture<>. The issue was that I was initializing Mediator on a base class an then adding the mock UnitOfWork on a derived class.
This cause the Mediator initialization to fail because one of the beheviours expected the UnitOfWork which at the time was not yet on the container.
Moving the initialization of Mediator after all the services have been added helped me resolve the issue and now all works as expected.
If you try the same thing, please make sure to include all the services in the container before initializing any objects that require those dependencies.
Thank you all those who had input.

I want to Create Xunit test for this controller. How can i do that

I have created small kind of xunit test case but I don't know how to create this controller which i have mention below.
public class PropertyController : ControllerBase
{
private readonly IMediator _mediator;
private readonly ILogger<PropertyController> _logger;
public PropertyController(IMediator mediator, ILogger<PropertyController> logger)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task<IActionResult> AddProperty([FromBody] AddPropertyCommand command)
{
bool commandResult = false;
_logger.LogInformation(
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({#Command})",
command.GetGenericTypeName(),
nameof(command.ModifiedUserId),
command.ModifiedUserId,
command);
commandResult = await _mediator.Send(command);
if (!commandResult)
{
return BadRequest();
}
return Ok();
}
I have created like this. i have mock the dependency and create a test case for add command is working fine or not
public class PropertyControllerTest
{
private readonly PropertyController _it;
private readonly Mock<IMediator> _mediatorMock;
private readonly Mock<ILogger<PropertyController>> _loggerPropertycontrollerMock;
public PropertyControllerTest()
{
_mediatorMock = new Mock<IMediator>();
_loggerPropertycontrollerMock = new Mock<ILogger<PropertyController>>();
_it = new PropertyController(_mediatorMock.Object, _loggerPropertycontrollerMock.Object);
}
[Fact]
public void it_Should_add_information_successfully_and_returns_200_status_result()
{
//How can i write xunit test case. I'm creating like this
_mediatorMock.Setup(x => x.Send().Returns(property);
}
The test below covers the 200 status result - a similar test for bad requests would be very similar.
[Fact]
public void it_Should_add_information_successfully_and_returns_200_status_result()
{
// Arrange
var expected = new AddPropertyCommand();
_mediatorMock.Setup(x => x.Send(It.IsAny<AddPropertyCommand>())).Returns(true);
// Act
var actionResult = _it.AddProperty(expected);
// Assert
actionResult.ShouldBeAssignableTo<OkResult>();
_mediatorMock.Verify(x => x.Send(expected));
}
N.B. actionResult.ShouldBeAssignableTo<OkResult>(); is written using the Shouldly assertion framework, you can swap that out for anything you like. The one built into XUnit would be like this: Assert.IsType(typeof(OkResult), actionResult);

Testing controller with xUnit

I have webapi where it needs to call some other endpoint and get data.
My current code as follows
//http client implementation
public interface IHttpClientFactory
{
HttpClient Create();
}
public class HttpClientFactory : IHttpClientFactory
{
private readonly ApplicationSettings _applicationSettings;
HttpClient _httpClient;
public HttpClientFactory(IOptions<ApplicationSettings> settings)
{
_applicationSettings = settings.Value;
}
public HttpClient Create()
{
if (_httpClient != null)
return _httpClient;
var client = new HttpClient()
{
BaseAddress = new Uri($"{_applicationSettings.BaseUrl}")
};
_httpClient = client;
return _httpClient;
}
}
public interface IGetItemsQuery
{
Task<IEnumerable<T>> Execute<T>(string url);
}
public class GetItemQuery: IGetItemsQuery
{
private readonly IHttpClientFactory _httpClientFactory;
public GetPhotosQuery(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<IEnumerable<T>> Execute<T>(string url)
{
using (var response = await _httpClientFactory.Create().GetAsync($"{url}").ConfigureAwait(false))
{
response.EnsureSuccessStatusCode();
var resp = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var items = JArray.Parse(resp);
return items.ToObject<T[]>();
}
}
In my controller part
private readonly IGetItemsQuery _getItemsQuery;
public HomeController(IGetItemsQuery getItemsQuery)
{
_getItemsQuery = getItemsQuery;
}
appsettings
"ApplicationSettings": {
"BaseUrl": "http://someendpoint.com/"
}
Startup
services.Configure<ApplicationSettings>(Configuration.GetSection("ApplicationSettings"));
services.AddScoped<IGetItemsQuery, GetPhotosQuery>();
services.AddScoped<IHttpClientFactory, HttpClientFactory>();
I want to try something like below in my test
[Fact]
public void Test_Index()
{
// Arrange
var itemsQuery = new Mock<IGetItemsQuery>();
var controller = new HomeController(itemsQuery.Object);
// Act
var result = controller.Index();
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
Assert.Null(viewResult.ViewName);
}
This is creating mock IGetItemsQuery but this isn't mocking the actual IHttpClientFactory.
Is there a way to do this
Based on your design with the abstracted dependencies there would be no need to mock a client factory in order to unit test the controller.
As you have done in your test, you mock IGetItemsQuery, but you have not set it up to behave as expected when invoked in the test.
If, for example, the controller method under test look something like this
private readonly IGetItemsQuery getItemsQuery;
public HomeController(IGetItemsQuery getItemsQuery) {
this.getItemsQuery = getItemsQuery;
}
public async Task<IActionResult> Index() {
var url = "....";
var items = await getItemsQuery.Execute<MyItem>(url);
return View(items);
}
Then an isolated unit test for the Index action as the method under test could look something like
[Fact]
public async Task Index_Should_Return_View_With_Items() {
// Arrange
var itemsQuery = new Mock<IGetItemsQuery>();
var items = new MyItem[] {
new MyItem(),
new MyItem()
};
itemsQuery.Setup(_ => _.Execute<MyItem>(It.IsAny<string>()))
.ReturnsAsync(items);
var controller = new HomeController(itemsQuery.Object);
// Act
var result = await controller.Index();
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
Assert.Null(viewResult.ViewName);
}

Testing masstransit consumer

I am trying to test masstransit consumer
my consumer look like this:
public class FilePolicyEvaluationConsumer : IFilePolicyEvaluationConsumer
{
public readonly IFilePolicyEvaluationCore _filePolicyEvaluationCore;
public readonly IRepositoryClient _repositoryClient;
public readonly IFPRepository _fPRepository;
public FilePolicyEvaluationConsumer()
{
}
public FilePolicyEvaluationConsumer(IFilePolicyEvaluationCore filePolicyEvaluationCore, IRepositoryClient repositoryClient, IFPRepository fPRepository)
{
_filePolicyEvaluationCore = filePolicyEvaluationCore;
_repositoryClient = repositoryClient;
_fPRepository = fPRepository;
}
public Task Consume(ConsumeContext<InvokePolicyOnFileMessage> context)
{
return Task.Run( async () => {
Guid fileId = context.Message.FileId;
FileMetadata fileMetaData = await _repositoryClient.GetFileMetadata(fileId);
_filePolicyEvaluationCore.Run(fileMetaData, context.Message.CorrelationId);
});
}
but when i configure the test like this
_harness = new InMemoryTestHarness();
FilePolicyEvaluationConsumer filePolicy = new FilePolicyEvaluationConsumer();
_consumer = _harness.Consumer<FilePolicyEvaluationConsumer>();
await _harness.Start();
await _harness.InputQueueSendEndpoint.Send(message1);
when i enter in the test to consumer all objects _filePolicyEvaluationCore,_repositoryClient,_fPRepository in the consumer are null, how can i inject to this objects?
It is null because you probably use a container in your production code and inject the constructor parameter. MassTransit has no injection built-in since it does not use any containers.
You can use the consumer factory overload to make it work:
_harness = new InMemoryTestHarness();
var filePolicy = new FilePolicyEvaluationConsumer();
_consumer = _harness.Consumer(
() => new FilePolicyEvaluationConsumer(filePolicy));

Categories

Resources