Mock returns null when mocking using Moq - c#

I might be missing something obvious here. Using Asp.Net core background service. The simplified setup is as follows:
SUT
public class OrderService: BaseOrderService
{
IConsumer<string, string> _consumer;
public OrderService(IBuilder consumerBuilder, ILogger logger) : base(logger)
{
_consumer = consumerBuilder.Build(() => Provider);
}
}
public abstract class BaseOrderService: BackgroundService
{
protected BaseOrderService(ILogger logger){}
....
....
}
IBuilder
public interface IBuilder
{
IConsumer<string, string> Build(Func<string> providerFunc);
}
Unit test
[Fact]
public async Task TestDescription()
{
var mockBuilder = new Mock<IBuilder>();
var mockConsumer = new Mock<IConsumer<string, string>>();
mockBuilder.Setup(x => x.Build(It.IsAny<string>)).Returns(mockConsumer.Object);
var sut = new OrderService(mockBuilder.Object, NullLogger.Instance);
await sut.StartAsync(default);
....
....
}
Question
Upon running my test, in the OrderService constructor, calling _consumer = _consumerBuilder.Build(()=> Provider) returns null, although I have provided the mock setup in the test.
What am I missing? Any pointers will be very much appreciated.

The wrong argument matcher was used.
The mocked member expected Func<string>
IConsumer<string, string> Build(Func<string> providerFunc);
But your code setup It.IsAny<string>
mockBuilder
.Setup(x => x.Build(It.IsAny<string>)) //<-- WRONG
.Returns(mockConsumer.Object);
It should be It.IsAny<Func<string>>()
mockBuilder
.Setup(x => x.Build(It.IsAny<Func<string>>())) //<--
.Returns(mockConsumer.Object);

Related

How to unit test a controller with XUnit, Moq and AutoFixture?

I have a controller with the following signature:
public CustomerTypeController(
IHttpContextAccessor accessor,
IPrincipalProvider provider,
IMapper mapper,
ILogger<CustomerTypeController> logger,
ICustomerTypeService customerTypeService)
{ }
I also set up an AutoDataAttribute:
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute()
: base(() =>
{
var fixture = new Fixture().Customize(new CompositeCustomization(
new AutoMoqCustomization(),
new SupportMutableValueTypesCustomization()));
fixture.Behaviors.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(x => fixture.Behaviors.Remove(x));
fixture.Behaviors.Add(new OmitOnRecursionBehavior());
return fixture;
})
{ }
}
For now my Theory looks like this:
[Theory, AutoMoqData]
public void GetWhenHasCustomerTypesShouldReturnOneCustomerType(
IFixture fixture,
[Frozen] Mock<ICustomerTypeService> service,
CustomerTypeController sut)
{
//Arrange
var items = fixture.CreateMany<Model.CustomerType>(3).ToList();
//Act
var result = sut.Get(1);
//Assert
Assert.IsType<OkResult>(result);
}
Do I need to setup the service with the items before getting an item from the controller? If yes, how is the service set up?
Use the [NoAutoProperties] attribute to decorate the controller parameter in the test method.
Refer to this answer for details.

Moq doesn't verify actions on DbSet and DbContext after adding new data

I have the following xunit test using Moq
[Fact]
public async void Add_Valid()
{
// Arrange
var mockSet = new Mock<DbSet<CategoryDao>>();
var mockContext = new Mock<Data.Context.AppContext>();
mockContext.Setup(m => m.Categories).Returns(mockSet.Object);
var categoryProfile = new CategoryVoProfile();
var configMapper = new MapperConfiguration(cfg => cfg.AddProfile(categoryProfile));
IMapper mapper = new Mapper(configMapper);
var service = new InDbCategoryService(mockContext.Object, mapper);
// Act
await service.Add(new CategoryVo() { Name = "CreatedName1" });
// Assert
mockSet.Verify(m => m.Add(It.IsAny<CategoryDao>()), Times.Once()); // DbSet verification
mockContext.Verify(m => m.SaveChanges(), Times.Once()); // DbContext verification
}
And it throws this error:
Moq.MockException:
Expected invocation on the mock once, but was 0 times: m => m.Add(It.IsAny())
Performed invocations:
Mock<DbSet:1> (m):
No invocations performed.
When I delete the DbSet verification line and ask to verify only the DbContext, it throws this:
Moq.MockException :
Expected invocation on the mock once, but was 0 times: m => m.SaveChanges()
Performed invocations:
MockAppContext:1 (m):
AppContext.Categories = InternalDbSet
DbContext.Add(CategoryDao)
DbContext.SaveChangesAsync(CancellationToken)
The simplified service looks like this:
public class InDbCategoryService : IDataServiceAsync<CategoryVo>
{
private readonly Data.Context.AppContext context;
private readonly IMapper mapper;
public InDbCategoryService(Data.Context.AppContext context, IMapper mapper)
{
this.context = context;
this.mapper = mapper;
}
public async Task Add(CategoryVo item)
{
context.Add(entity: mapper.Map<CategoryDao>(item));
await context.SaveChangesAsync();
}
}
The category profile:
public class CategoryVoProfile : Profile
{
public CategoryVoProfile()
{
CreateMap<CategoryDao, CategoryVo>()
.ReverseMap();
}
}
Database context:
public class AppContext : DbContext
{
public AppContext() { }
public AppContext (DbContextOptions<AppContext> options) : base(options) { }
public virtual DbSet<CategoryDao> Categories { get; set; }
}
I've used this microsoft docs example for my test, but it's clear I'm missing something. Any help or advice is appreciated.
You are not testing the methods that you've called in your service. Your add method:
public async Task Add(CategoryVo item)
{
context.Add(entity: mapper.Map<CategoryDao>(item));
await context.SaveChangesAsync();
}
You'll note you're calling the DbContext.Add not the context.Categories.Add which is what you verify in your test:
mockSet.Verify(m => m.Add(It.IsAny<CategoryDao>()), Times.Once());
The same is true for your SaveChanges. You're verifying the synchronous version but calling the async one. So you need to modify what you're verifying to match what you're using.

Mocking and testing the LogError message using Moq and xUnit

I have a class level ILogger which is set up with the ILoggerFactory in the constructor. The logger is then used within a method within that class and this works perfectly.
I am struggling on how to Mock the ILogger and ILoggerFactory so I can unit test the LogError message. Can anyone show me an example of this?
I am using xUnit and Microsoft.Extentions.Logging for the loggin
//This is my unit test project
[Fact]
public void TestLogErrorMessage()
{
MyClass myClass = new MyClass (MockLoggerFactory().Object);
var result = myClass.Mymethod("a")
//How do I test the LogError message???
}
//Not sure if this is correct
private Mock<ILoggerFactory> MockLoggerFactory()
{
Mock<ILoggerFactory> mockLoggerFactory = new
Mock<ILoggerFactory>(MockBehavior.Default);
mockLoggerFactory.Setup(x => x.CreateLogger(It.IsAny<string>()))
.Returns(MockLogger().Object);
return mockLoggerFactory;
}
private Mock<ILogger> MockLogger()
{
var logger = new Mock<ILogger>();
return logger;
}
//This is the class/method i need to test
private readonly ILogger logger;
public MyClass(ILoggerFactory loggerFactory)
{
if (loggerFactory != null)
{
this.logger = loggerFactory.CreateLogger<MyClass>();
}
}
public string Mymethod(string msg)
{
if(msg = "a")
{
this.logger.LogError($"error");
}
return "a string";
}
This is what finally worked for me, successful verification of LogError call:
var loggerMock = new Mock<ILogger>();
Expression<Action<ILogger>> logAction = x =>
// You can change this up with other severity levels:
x.Log<It.IsAnyType>(LogLevel.Error,
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>());
loggerMock.Setup(logAction).Verifiable();
// Call a method that calls LogError here.
loggerMock.Object.LogError("test");
loggerMock.Verify(logAction, Times.Once);
You could do something like this
(note: this does not work in this specific case because LogError is an extension method, see below for an update):
Mock<ILogger> _logger; // declare it somewhere where your test can access it
[Fact]
public void TestLogErrorMessage()
{
MyClass myClass = new MyClass(MockLoggerFactory().Object);
var result = myClass.Mymethod("a")
_logger.Verify();
}
private Mock<ILoggerFactory> MockLoggerFactory()
{
_logger = new Mock<ILogger>();
_logger.Setup(log => log.LogError(It.IsAny<string>())).Verifiable();
Mock<ILoggerFactory> mockLoggerFactory = new Mock<ILoggerFactory>(MockBehavior.Default);
mockLoggerFactory.Setup(x => x.CreateLogger(It.IsAny<string>()))
.Returns(_logger.Object);
return mockLoggerFactory;
}
The key thing to note is the call to Verifiable() after setting up the ILogger mock. You can read a bit more about Verifiable() in this SO question. After your test ran, you can check whether the method was called by calling .Verify() on the mock.
Depending on your needs, you could set this up in the constructor of your test class (if you need it for all/most tests) or directly inside your test method. You could also return it alongside the ILoggerFactory. The point is to hold onto the logger so you can verify against it that the method was called.
For your specific case (trying to verify calls to ILogger.LogError) you must not mock LogError, but the underlying method ILogger.Log. That could look like this:
var formatter = new Func<It.IsAnyType, Exception, string>((a, b) => "");
m.Setup(x => x.Log<It.IsAnyType>(LogLevel.Error,
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.IsAny<Exception>(),
formatter))
.Verifiable();
Another alternative would be to make a fake implementation of ILogger and return that from the Mock<ILoggerFactory>:
mockLoggerFactory.Setup(_ => _.CreateLogger(It.IsAny<string>())
.Returns(new FakeLogger());
public class FakeLogger : ILogger
{
public static bool LogErrorCalled { get; set; }
public IDisposable BeginScope<TState>(TState state)
{
throw new NotImplementedException();
}
public bool IsEnabled(LogLevel logLevel) => true;
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if(logLevel == LogLevel.Error)
{
LogErrorCalled = true;
}
}
}
And then after your test you can check whether FakeLogger.LogErrorCalled is true. This is simplified for your specific test - you can get more elaborate than this of course.

Mocked method do not pass correct value

I am trying to understand how mocking works in Xunit with AutoFixture. I have created Service and Repository classes and their interfaces. Mocked method should pass value which is different from default value.
Mocked method always pass default values instead of values which i am writing in ".Returns()". I have tried AutoConfiguredMoqCustomization but it provides completely random values which i can't get back.
Repository.cs
public class Repository : IRepository
{
public int GetInt()
{
return 999;
}
}
Service.cs
public class Service : IService
{
private readonly Repository _repository;
public Service()
{
_repository = new Repository();
}
public string GetStringFromInt()
{
return _repository.GetInt().ToString();
}
}
Test
[Fact]
public void Test()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var repositoryMock = fixture.Create<Mock<IRepository>>();
var service = fixture.Create<Service>();
repositoryMock.Setup(x => x.GetInt()).Returns(1);
var act = service.GetStringFromInt();
Assert.Equal("1", act);
}
As you see value by default in Repository is 999 and I am expecting 1 from repositoryMock but result is "999" instead of "1".
Ow I have understood my problem. When I declare parameters with auto moq testing service must be AFTER all mocked repositories
Test
[Theory, AutoMoqData]
public void Test([Frozen] Mock<IRepository> repositoryMock, Service service)
{
...
}
Attribute
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute() : base(GetDefaultFixture)
{
}
private static IFixture GetDefaultFixture()
{
return new Fixture().Customize(new AutoMoqCustomization());
}
}
You should freeze your mock first. When you call Create on AutoFixture, it will create you a new instance every time. Try the following in the modified code (where you are using an interface of the type in your constructor).
public class ServiceTests
{
private readonly IFixture fixture = new Fixture().Customize(new AutoMoqCustomization());
public ServiceTests()
{
fixture.Register<IService>(() => fixture.Create<Service>());
}
[Fact]
public void Test()
{
// Arrange
var repositoryMock = fixture.Freeze<Mock<IRepository>>();
repositoryMock.Setup(x => x.GetInt()).Returns(1);
var service = fixture.Create<IService>();
// Act
var act = service.GetStringFromInt();
// Verify
Assert.Equal("1", act);
}
}
To check that you have set up autofixture correctly, you can try the following in the unit test in future.
var repo1 = fixture.Create<IRepository>();
var repo2 = fixture.Create<IRepository>();
Assert.Equal(repo1.GetHashCode(), repo2.GetHashCode());
If the above fails, that indicates that you have not frozen a type. These lines of code have saved me so much head scratching in the past...
You are doing DI wrong, you are not injecting Repository into Your serice.
Try like this.
public class Repository : IRepository
{
public int GetInt()
{
return 999;
}
}
public class Service : IService
{
IRepository Repository;
public Service(IRepository repository)
{
this.Repository = repository;
}
public string GetStringFromInt()
{
return Repository.GetInt().ToString();
}
}
Now when you mock IRepository, you can add it to Service.
You are using a new Repository() in constructor, so you are using that implementation

Moq: setup a generic method with mocked parameters

I've been trying to write a few tests in NUnit for my generic methods, without success. I hope I can make my situation clear, since I had to heavily paraphrase my code.
DoBusinessLogic() is the method I want to test. It calls two other methods from the base class:
public class MySvc : BaseSvc, IMySvc
{
private readonly IMyRepo _repo;
private readonly IMyConnectorClass _connector
public MySvc(IMyRepo repo) : base(repo)
{
_repo = repo;
_connector = _repo.GetConnector();
}
public async Task DoBusinessLogic(int id1, int id2){
bool doesFirstObjectExist = await CheckMainObjExists<Foo>(id1, _connector);
await CheckSubObjExists<Bar>(id2, _connector);
// further business logic
}
}
Those methods, in turn, call the same repository method, but have different logic after it:
public abstract class BaseSvc : IBaseSvc
{
private readonly IBaseRepo _repo
protected BaseSvc(IBaseRepo repo)
{
_repo = repo;
}
protected async Task<bool> CheckMainObjExists<T>(int? id, IMyConnectorClass connector)
{
return await _repo.GetObjectByIdAsync<T>(Id, connector) != null;
}
protected async Task CheckSubObjExists<T>(int? id, IMyConnectorClass connector)
{
if (await _repo.GetObjectByIdAsync<T>(Id, connector) == null)
{ throw new Exception("Object not found!"); }
}
}
Next, I want to write unit a test for DoBusinessLogic() in the MySvc class. Unfortunately, it seems I can't mock the responses from the repository.
[TestFixture]
public class MySvcTests
{
private MySvc _svc;
private Mock<IMyRepo> _repoMock;
private Mock<IMyConnectorClass> _connectorMock;
[SetUp]
public void SetUp()
{
_repoMock = new Mock<IMyRepo>() {};
_connectorMock = new Mock<IMyConnectorClass>();
_repo.SetUp(r => r.GetConnector()).Return(_connectorMock.Object);
_svc = new MySvc(_repoMock);
}
/*
My intent in this test, is to make CheckMainObjExists() pass,
but make CheckSubObjExist() fail.
*/
[Test]
public async Task DoBusinessLogic_If2ndObjectNotExist_ThrowException()
{
// This should return an object
_repoMock.Setup(r => r.GetObjectByIdAsync<Foo>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync(new Foo());
// This should return null
_repoMock.Setup(r => r.GetObjectByIdAsync<Bar>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync((Bar) null);
Assert.Throws<Exception>(await _svc.DoBusinessLogic());
}
}
However, when I run the test, both methods that I set up for my mock repo return null, whereas I expect a "true" from the first.
I do not know where the problem is situated, but I have my suspicions:
Is it possible to setup a method, using a mocked object as a parameter? In this case, is it possible to use _connectorMock.Object as a setup parameter?
Is it possible to setup the same generic method multiple times, but for a different type each time? It's first setup for Foo, then for Bar.
I just tested this code and it runs as expected. Now I had to make a lot of assumptions just to get the code to compile and run which would mean that my test of your code is flawed as I may have fixed something that was omitted in your example.
I made no changes to your test setup code, which worked.
[TestClass]
public class MySvcTests {
[TestMethod]
[ExpectedException(typeof(Exception))]
public async Task DoBusinessLogic_If2ndObjectNotExist_ThrowException() {
var _repoMock = new Mock<IMyRepo>() { };
var _connectorMock = new Mock<IMyConnectorClass>();
_repoMock.Setup(r => r.GetConnector()).Returns(_connectorMock.Object);
var _svc = new MySvc(_repoMock.Object);
// This should return an object
_repoMock.Setup(r => r.GetObjectByIdAsync<Foo>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync(new Foo());
// This should return null
_repoMock.Setup(r => r.GetObjectByIdAsync<Bar>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync((Bar)null);
await _svc.DoBusinessLogic(0, 0);
}
}

Categories

Resources