5

I have a model in Entity Framework. I want to make sure that Application Service is reading the Generic Repository pointing to the database correctly. How would I test this in Xunit, I am kind of stuck ? Receiving the error below, maybe I need to rewrite code. Need to validate that App service can read example data row below in InMemorydatabase.

Model:

public partial class Department
{
    public int DepartmentId { get; set; }
    public string DepartmentCode { get; set; }
    public string DepartmentName { get; set; }
    .... plus additional
}

DTO:

public class DepartmentDto
{
    public int DepartmentId { get; set; }
    public string DepartmentCode { get; set; }
    public string DepartmentName { get; set; }
}

Base Repository from IRepository:

    public async Task<T> GetAsync(int id)
    {
        return await Table.FindAsync(id);
    }

**Constructor:**

    public BaseRepository(DbContext context)
    {
        _context = context;
        Table = _context.Set<T>();
    }

Service: DepartmentAppService.cs

    public async Task<DepartmentDto> GetDepartmentById(int id)
    {
        var department = await departmentRepository.GetAsync(id);
        var departmentDto = mapper.Map<Department, DepartmentDto>(department);
        return departmentDto;
    }

**Constructor:**

   public DepartmentAppService(IRepository<Department> departmentRepository, IMapper mapper)
    {
        this.departmentRepository = departmentRepository;
        this.mapper = mapper;
    }

Attempted Test Code:

public async Task Get_DepartmentById_Are_Equal()
    {
        var options = new DbContextOptionsBuilder<TestContext>()
            .UseInMemoryDatabase(databaseName: "TestDatabase")
            .Options;

        var context = new TestContext(options);

        Mock<IRepository<Department>> departmentRepositoryMock = new Mock<IRepository<Department>>();
        Mock<IMapper> mapperMock = new Mock<IMapper>();
        Mock<DepartmentAppService> departmentAppServiceMock = new Mock<DepartmentAppService>(departmentRepositoryMock, mapperMock);

        context.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" });

        var test = await departmentAppServiceMock.GetDepartmentById(5);

        Assert.Equal("123", test.DepartmentCode);

Receiving Error: How to Fix?

'Mock<DepartmentAppService>' does not contain a definition for 'GetDepartmentById' and no accessible extension method 'GetDepartmentById' accepting a first argument of type 'Mock<DepartmentAppService>' could be found (are you missing a using directive or an assembly reference?)  

Also have dependency injection in the program. Open to code being rewritten as required,

  • Can you add the constructor of your service and repository – garethb Jul 31 '19 at 00:40
  • What was the instance of `IMapper` ?, you were generated directly instance of `Mock` but interface can not work alone it must be derived from a class. Also i dont know your repository structure but `IRepository` must be referenced from like this `Mock> departmentRepositoryMock = new Mock>();`, (Repository class not interface) – Mustafa Salih ASLIM Jul 31 '19 at 01:10
  • Also, you must bind `TestContext` to your `Repository class` which derived from `IRepository` otherwise IRepository doesn't know which `dbContext` should be use. – Mustafa Salih ASLIM Jul 31 '19 at 01:18
  • yeah, working on that right now, feel free to scrap and rewrite code if needed, learning myself –  Jul 31 '19 at 01:23

1 Answers1

3

You don't want to create a mock of the DepartmentAppService. You want to create an instance. The method you are testing requires an instance of the real object, not a mock. You want to test the actual object's code, mocks don't run any code but only return fake data.

        var config = new MapperConfiguration(cfg =>
        {
            cfg.AddProfile(sharedServicesProfile);
        });
        mapper = config.CreateMapper();

var departmentAppService = new DepartmentAppService(departmentRepositoryMock.Object, mapperMock.Object);

If you are using an InMemory database and you are actually getting the data for your repository from that InMemory database, you don't want to Mock your repository either as you are hitting a physical database and returning real data and not mocked data.

To be honest, I think you need to read up a bit more on testing and mocking. Looks like there are a few core concepts missing... This is one way to write what you are trying to do.

AutoMapper.Mapper.Initialize(m => m.AddProfile<YOUR AUTOMAPPER PROFILE>());
AutoMapper.Mapper.AssertConfigurationIsValid();
var options = new DbContextOptionsBuilder<TestContext>()
        .UseInMemoryDatabase(databaseName: "TestDatabase")
        .Options;

var context = new TestContext(options))
context.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" });
context.SaveChanges();    

var departmentRepository = new Repository<Department>>(context);
var departmentAppService = new DepartmentAppService(departmentRepository, mapper);

var test = await departmentAppService.GetDepartmentById(5);

Assert.Equal("123", test.DepartmentCode);

You may also want to think about disposing your context or wrapping it in a using. If the code doesn't run correctly if you copy paste there may be a few tweaks you need to make but it should be fairly close.

As you can see from the code above you still need to learn when to mock and when not to mock! No mocks were needed. You are also doing an integration test, if you didn't want an integration test you could remove the context stuff and mock the Repository and setup the mock to return the department.

garethb
  • 3,951
  • 6
  • 32
  • 52
  • @AlanWalker, this is a good tutorial I used to help me when I was starting out https://www.scottbrady91.com/Entity-Framework/Entity-Framework-Core-In-Memory-Testing – garethb Jul 31 '19 at 00:15
  • The blog post I mentioned is EXACTLY the same thing you are trying to do.... For example you are writing to the database without calling save changes. You need to wrap your database context instance in a using as in the blog, you don't need mocking for your `integration` test, etc... – garethb Jul 31 '19 at 00:19
  • @AlanWalker How do you initialise automapper in your startup? – garethb Jul 31 '19 at 01:17
  • cool, thanks I need to learn how to call ICOllection services, initialize in unit test, and I should be done, public ApplicationServicesMappingProfile() { CreateMap() .ReverseMap(); public void InitServices(IServiceCollection services, IConfiguration configuration) { services.AddTransient(); services.AddMappingProfile(); } –  Jul 31 '19 at 03:13
  • when doing this, AutoMapper.Mapper.Instance, receiving error, Message: System.InvalidOperationException : Mapper not initialized. Call Initialize with appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance. –  Jul 31 '19 at 03:40
  • @AlanWalker Did you use the first line to initialise automapper and replace the text 'YOUR AUTOMAPPER PROFILE' with your profile class (ApplicationServicesMappingProfile)? – garethb Jul 31 '19 at 06:32
  • got some kind of error here, https://stackoverflow.com/questions/51625446/mapper-not-initialized-call-initialize-with-appropriate-configuration anyways its fixed now, accepted answer and gave points –  Jul 31 '19 at 07:11