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.
Related
I'm trying to mock using Moq, xUnit,.Net 6, Entity framework Core, I have this service:
public class CompanyService : ICompanyService
{
private readonly ICompanyRepository _repository;
public CompanyService(ICompanyRepository repository)
{
_repository = repository;
}
public async Task<Company> Create(Company Company) => await _repository.Create(Company);
}
this is the company service interface:
public interface ICompanyService
{
Task<Company> Create(Company Company);
}
this is my company repository class:
public class CompanyRepository : ICompanyRepository
{
private readonly ApplicationDbContext _context;
public CompanyRepository(ApplicationDbContext context)
{
_context = context;
}
public async Task<Company> Create(Company Company)
{
Company.Id = Guid.NewGuid();
await _context.Companies.AddAsync(Company);
await _context.SaveChangesAsync();
return Company;
}
}
and this is its interface:
public interface ICompanyRepository
{
Task<Company> Create(Company Company);
}
this is my DbContext:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions options): base(options)
{}
public DbSet<Company> Companies { get; set; }
}
and finally this is my class for testing:
public class CompanyServiceTest
{
private readonly CompanyService _sut;
private readonly Mock<ICompanyRepository> _companyRepositoryMock = new Mock<ICompanyRepository>();
public CompanyServiceTest()
{
_sut = new CompanyService(_companyRepositoryMock.Object);
}
[Fact]
public async void CreateACompanyShouldWorks()
{
//Arrange
var CompanyEntity = new Company();
CompanyEntity.Name = "Test Company";
//Act
var companyCreated = await _sut.Create(CompanyEntity);
//Assert
Assert.NotNull(companyCreated);
_companyRepositoryMock.Verify(r => r.Create(CompanyEntity));
}
}
but when I run the test I get this message error:
"Assert.NotNull() Failure"
Stack Trace:
CompanyServiceTest.CreateACompanyShouldWorks() line 31
<>c.b__128_0(Object state)
I don't know if I have to mock the ApplicationDbContext, or if I have to mock anything else, I'm Using xUnit, .Net 6, Entity framework core, also I have downloaded the Moq package.
You just forgot to setup the mock inside the Arrange phase
_companyRepositoryMock
.Setup(r => r.Create(It.IsAny<CompanyEntity>()))
.ReturnsAsync(expectedCompany);
OR
_companyRepositoryMock
.Setup(r => r.Create(It.IsAny<CompanyEntity>()).Result)
.Returns(expectedCompany);
If you don't setup the mock then it will return the default value.
If you change the MockBehaviour to strict then it will throw exception.
UPDATE #1
in the case of the method GetAllCompanies I have to create a List<Company>() and then return them ? and in the method GetCompanyById I have to create an object of Company and returns it ? I mean xUnit does not go to the database using my repository?
The short answer is yes you have to mock all the methods that you want to use on your dependency. Since the CompanyService is relying correctly on abstraction (ICompanyRepository interface) rather than on the implementation (CompanyRepository class) that's why you are not depending on the Entity Framework directly.
If you want to test your repository tier/layer/module than you have to mock the DbContext to avoid database calls. There are tons of nuget packages which can be used to ease the mocking burden.
This is the solution:
//Arrange
var CompanyEntity = new Company();
CompanyEntity.Name = "Test Company";
CompanyEntity.Id = Guid.NewGuid();
CompanyEntity.Employees = new HashSet<Employee>();
//arrange
_companyRepositoryMock
.Setup(r => r.Create(It.IsAny<Company>()))
.ReturnsAsync(CompanyEntity);
//Act
var companyCreated = await _sut.Create(CompanyEntity);
//Assert
Assert.NotNull(companyCreated);
_companyRepositoryMock.Verify(r => r.Create(companyCreated));
I am a totally newbie to Mediator pattern and Moq. I have an API controller that has an async task method and want to mock this method
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class UsersController : UsersControllerBase
{
private readonly IMediator _mediator;
public UsersController(IMediator mediator)
{
this._mediator = mediator;
}
[HttpGet("GetUsers")]
public async Task<ActionResult<ICollection<User>>> GetUsers(CancellationToken cancellationToken)
{
var result = await _mediator.Send(new UserGetRequest(),
cancellationToken).ConfigureAwait(false);
return DataOrProblem(result);
}
}
I am trying to create or mock this method and not sure how to do it? Here is what I have tried
public class GetUsersTest
{
//Arrange
private readonly Mock<IMediator> _mediator = new Mock<IMediator>();
private readonly Mock<UsersController> _sut;
public GetUsersTest()
{
_sut = new Mock<UsersController>(_mediator);
}
}
If the controller is the subject under test then do not mock it. Create an actual instance, injecting the mocked mediator.
public class UsersControllerTest {
public async Task GetUsersTests() {
//Arrange
Mock<IMediator> mediator = new Mock<IMediator>();
UsersController sut = new UsersController(mediator.Object);
//... setup the behavior of the mocked mediator
ICollection<User> expected = //...provide fake data here
mediator
.Setup(s => s.Send<UserGetRequest>(It.IsAny<UserGetRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(expected);
//Act
ActionResult<ICollection<User>> result = await sut.GetUsers(CancellationToken.None);
//Assert
//assert the expected behavior with what actually happened
}
}
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);
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.
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