unit test base controller with ILogger - c#

I have a base controller (i didn't create it btw) in my net core api that basically starts with following:
public abstract class MyBaseController<T> : ControllerBase where T : MyBaseController<T>
{
private ILogger<T> _logger;
protected ILogger<T> Logger => _logger ?? (_logger = HttpContext?.RequestServices.GetService<ILogger<T>>());
}
When i am unit testing my other controller that inherits the base controller how do deal with this logger?
currently my unit test class has a constructer with something like
_controller = new cartController(_cartService);
but then i get stuck.
I will be using xUnit and Moq in the test project.
Any help is appreciated.Thanks

Here's a minimal example from this article on how to inject an ILogger dependency and then verify a call afterward with Moq:
public class LogTest
{
private readonly ILogger _logger;
public const string InformationMessage = "Test message";
public const string ErrorMessage = "Not implemented {recordId}";
public LogTest(ILogger<LogTest> logger)
{
_logger = logger;
}
public void Process()
{
_logger.LogInformation(InformationMessage);
}
}
_loggerMock.Verify(l => l.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.IsAny<Exception>(),
(Func<It.IsAnyType, Exception, string>)It.IsAny<object>()), Times.Exactly(1));

In general you should rely on DI in tests as well as in runtime. The following library contains a test logger which you may use in tests: https://www.nuget.org/packages/com.github.akovac35.Logging.Testing/
Usage samples are available here: https://github.com/akovac35/Logging.Samples
Disclaimer: I am the author of the above.
Basically you would proceed as follows:
Use NullLogger by default:
public abstract class MyBaseController<T> : ControllerBase
{
private ILogger _logger = NullLogger.Instance;
protected MyBaseController(ILogger<MyBaseController<T>> logger = null)
{
if (logger != null) _logger = logger;
}
}
Derived classes should inject logger:
public class MyBaseControllerVariant<T> : MyBaseController<T>
{
private ILogger _logger = NullLogger.Instance;
public MyBaseControllerVariant(ILogger<MyBaseControllerVariant<T>> logger = null, ILogger<MyBaseController<T>> baseLogger = null): base(baseLogger)
{
if (logger != null) _logger = logger;
}
}
Now wire up everything:
using com.github.akovac35.Logging.Testing;
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
using Shared.Mocks;
using System;
namespace TestApp
{
[TestFixture]
public class TestLoggingExamples
{
[OneTimeSetUp]
public void OneTimeSetUp()
{
customOnWrite = writeContext => {
Console.WriteLine(writeContext);
};
customOnBeginScope = scopeContext => {
Console.WriteLine(scopeContext);
};
serviceCollection = new ServiceCollection();
serviceCollection.AddTransient(typeof(MyBaseControllerVariant<>));
// Register TestLogger using extension method
serviceCollection.AddTestLogger(onWrite: customOnWrite, onBeginScope: customOnBeginScope);
}
private IServiceCollection serviceCollection;
private Action<WriteContext> customOnWrite;
private Action<ScopeContext> customOnBeginScope;
[Test]
public void Test_WithLoggingToTestConsole_Works()
{
// The service provider should be defined on per-test level or logger writes will accumulate and may result in OOM - clean them with testSink.Clear()
var serviceProvider = serviceCollection.BuildServiceProvider();
var controller = serviceProvider.GetRequiredService<MyBaseControllerVariant<object>>();
controller.Invoke();
var testSink = serviceProvider.GetRequiredService<ITestSink>();
Assert.IsTrue(testSink.Writes.Count > 0);
Assert.IsTrue(testSink.Scopes.Count > 0);
}
}
}

Related

Aspnetboilerplate ObjectMapper or Logger is null

I have a small assembly that utilises the following NuGet packages Abp, Abp.AspNetCore and Abp.AbpAutoMapper.
For reference I am using Abp 6.4.0
I have added the depends to the module, the main service is also below.
The constructor inject is valid for both IObjectMapper and Ilogger. Whereas the inherited DomainService and ApplicationService for ObjectMapper and Logger are always null / NullLogger.Instance or NullObjectMapper.Instance.
[DependsOn(typeof(AbpAutoMapperModule))]
public class ServiceModule:AbpModule {
// other methods and logic
public override void PostInitialize(){
var mainService = Configuration.Get<IMainService>();
Task.Run(() =>
{
mainService.Start();
});
base.PostInitialize();
}
}
public class MainService : ApplicationService, IMainService
{
public MainService(IObjectMapper mapper, ILogger logger)
{
if (Logger == NullLogger.Instance)
{
Debug.WriteLine("No logger configured.....");
}
if (ObjectMapper == NullObjectMapper.Instance)
{
Debug.WriteLine("No mapper configured.....");
}
}
}
// OR domain service
public class MainService : DomainService, IMainService
{
public MainService(IObjectMapper mapper, ILogger logger)
{
if (Logger == NullLogger.Instance)
{
Debug.WriteLine("No logger configured.....");
}
if (ObjectMapper == NullObjectMapper.Instance)
{
Debug.WriteLine("No mapper configured.....");
}
}
}
Any ideas on how this can be resolved?
Thanks
I think, u have to register your injections. Something like this:
public class ServiceModule:AbpModule {
// other methods and logic
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddScoped<IMainService>(sp => sp.GetRequiredService<MainService>());
}
public override void PostInitialize(){
var mainService = Configuration.Get<IMainService>();
Task.Run(() =>
{
mainService.Start();
});
base.PostInitialize();
}
}
DomainService and ApplicationService allow IObjectMapper and ILogger to be property-injected.
If MainService requires non-null object pattern instances to work properly, you can proactively assign your constructor-injected instances:
public MainService(IObjectMapper mapper, ILogger logger)
{
ObjectMapper = mapper;
Logger = logger;
}
References:
https://aspnetboilerplate.com/Pages/Documents/Dependency-Injection#property-injection-pattern
https://aspnetboilerplate.com/Pages/Documents/Logging#getting-the-logger

Injecting logger from DI to object created with factory

I need to add logger (ILogger) to existing object of MyDbConnection, this object is created from Factory which is registered in NET Core DI together with MyOptions class
public class MyFactory : IMyFactory
{
private readonly MyOptions _options;
public MyFactory(MyOptions options)
{
_options = options;
}
public MyDbConnection CreateDbA() => new MyDbConnection(_options.ConnStrA);
public MyDbConnection CreateDbB() => new MyDbConnection(_options.ConnStrB);
public MyDbConnection CreateDbC() => new MyDbConnection(_options.ConnStrC);
}
Factory is then injected into service, which then use it to create object and do something
public class MyService : IMyService
{
public MyService(IMyFactory factory)
{
var a = factory.CreateDbA();
var b = factory.CreateDbB();
a.DoSomething();
b.DoSomething();
}
}
MyDbConnection looks like this:
public class MyDbConnection
{
private string connStr;
//private ILogger logger;
//public MyDbConnection(string connStr, ILogger logger)
public MyDbConnection(string connStr)
{
this.connStr = connStr;
//this.logger = Logger;
}
public void DoSomething()
{
//logger.LogWarning();
}
}
Problem is that I can't just add ILogger and inject it from DI container because factory is using 'new' to create MyDbConnection, without using DI. My only solution currently is to use static logger instead of injected one, but that seems to be a bad solution. Is there another way around this? How it should be done properly?
The design will need to be refactored to be able to get the desired behavior.
First MyDbConnection should be refactored accordingly to depend on the appropriate logger
For example
public class MyDbConnection {
private string connStr;
private ILogger logger;
public MyDbConnection(string connStr, ILogger<MyDbConnection> logger) {
this.connStr = connStr;
this.logger = logger;
}
public void DoSomething() {
logger.LogWarning();
}
}
Then the factory refactored to use ActivatorUtilities to initialize and inject the necessary dependencies via an injected service provider.
public class MyFactory : IMyFactory {
private readonly MyOptions options;
private readonly IServiceProvider services;
public MyFactory(MyOptions options, IServiceProvider services) {
this.options = options;
this.services = services;
}
public MyDbConnection CreateDbA() => ActivatorUtilities.CreateInstance<MyDbConnection>(services, options.ConnStrA);
public MyDbConnection CreateDbB() => ActivatorUtilities.CreateInstance<MyDbConnection>(services, options.ConnStrB);
public MyDbConnection CreateDbC() => ActivatorUtilities.CreateInstance<MyDbConnection>(services, options.ConnStrC);
}
In the example above the specific dependencies are provided just as before when they were manually initialized. All other dependencies (like the logger) will be resolved via the service provider and injected into the target class when it is being initialized.
This way dependency injection can be maintained and manual initialization of MyDbConnection can be avoided.

How can I mock instance in ASP.NET Core unit test using Moq?

Writing unit test with nunit in asp.net core service, I try to write controller unit test.
[ApiController]
public class ConvertController : ControllerBase
{
private readonly ILogger<ConvertController> _logger;
private readonly Factory.IWorkbookFactory _workbookFactory;
public ConvertController(ILogger<ConvertController> logger, Factory.IWorkbookFactory workbookFactory)
{
_logger = logger;
_workbookFactory = workbookFactory;
}
[HttpPost]
[Route("api/v1/[controller]/pdf")]
public ConvertResponse Post(ConvertRequest req)
{
ConvertResponse res = new ConvertResponse();
res.OutputData = new Converter(_workbookFactory.newInstance()).ExcelToPDF(req.InputData);
return res;
}
}
In this case, can I mock whole Converter instance?
#naruke, in order to mock `Converter' type it need to be injected so the code shall have to be modified as below.
Firstly, implement the Converterlike this.
Create interface IConverter.
public interface IConverter
{
/*OutPut */ ExcelToPDF(/* input*/);
}
then implement
class Converter:IConverter
{
....
}
Now no need to inject IWorkbookFactory
[ApiController]
public class ConvertController : ControllerBase
{
private readonly ILogger<ConvertController> _logger;
//private readonly Factory.IWorkbookFactory _workbookFactory;
private readonly IConverter _converter;
public ConvertController(ILogger<ConvertController> logger,IConverter converter // Factory.IWorkbookFactory workbookFactory)
{
_logger = logger;
//_workbookFactory = workbookFactory;
_converter=converter;
}
[HttpPost]
[Route("api/v1/[controller]/pdf")]
public ConvertResponse Post(ConvertRequest req)
{
ConvertResponse res = new ConvertResponse();
res.OutputData = _converter.ExcelToPDF(req.InputData);
return res;
}
}
hope this helps.

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);

Categories

Resources