In ASP.NET Core 6, I'm using MariaDB and have a Controller that injects IService via dependency injection in its constructor. The Service injects its IRepository, which inherits from a GenericRepository that contains the DBContext injection. I want to use an in-memory database during testing, so I created DataBaseFixture : IDisposable, which also populates some data. However, I'm having difficulty implementing the tests and specifying that the context from DataBaseFixture should be used.
ArtistaController.cs
[Route("api/[controller]")]
public class ArtistaController : ControllerBase
{
private readonly IArtistaService _artistaService;
public ArtistaController(IArtistaService context)
{
_artistaService = context;
}
...
}
ArtistaService.cs
public class ArtistaService : IArtistaService
{
private readonly IArtistaRepository _artistaRepository;
private readonly IContextRepository _contextRepository;
public ArtistaService(IArtistaRepository artistaRepository, IContextRepository contextRepository)
{
_artistaRepository = artistaRepository;
_contextRepository = contextRepository;
}
...
}
ArtistaRepository.cs
public class ArtistaRepository : GenericRepository<ArtistaModel>, IArtistaRepository
{
public ArtistaRepository(OrientoonContext context) : base(context)
{
}
}
GenericRepository.cs
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
protected readonly OrientoonContext _context;
protected readonly DbSet<T> _dbSet;
public GenericRepository(OrientoonContext context)
{
_context = context;
_dbSet = context.Set<T>();
}
...
}
DataBaseFixture.cs
public class DataBaseFixture : IDisposable
{
public OrientoonContext Context { get; private set; }
public DataBaseFixture()
{
var options = new DbContextOptionsBuilder<OrientoonContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
Context = new OrientoonContext(options);
SeedDatabase();
}
private void SeedDatabase()
{
Context.Artista.AddRange(
new ArtistaModel { Id = "1", nome = "Oda" },
new ArtistaModel { Id = "2", nome = "Echiro" },
new ArtistaModel { Id = "3", nome = "Echiro Oda" }
);
Context.SaveChanges();
}
public void Dispose()
{
Context.Database.EnsureDeleted();
Context.Dispose();
}
}
The result is always returning null, and I've tried various approaches.
ArtistaControllerTest.cs
public class ArtistaControllerTest : IClassFixture<DataBaseFixture>
{
private readonly DataBaseFixture _fixture;
private readonly Mock<IArtistaService> _artistaServiceMock;
private readonly ArtistaController _controller;
public ArtistaControllerTest(DataBaseFixture fixture)
{
_fixture = fixture;
_artistaServiceMock = new Mock<IArtistaService>();
_artistaServiceMock.Setup(service => service.GetAsync(It.IsAny<string>())).ReturnsAsync((string id) =>
{
var artista = _fixture.Context.Artista.FirstOrDefault(a => a.Id == id);
return new ArtistaForm { Id = artista.Id, Nome = artista.nome };
});
_controller = new ArtistaController(_artistaServiceMock.Object);
}
[Fact]
public async void GetArtista()
{
var artistaId = "1";
var expectedArtista = new ArtistaModel { Id = artistaId, nome = "Oda" };
var result = await _controller.Get(artistaId);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result.Result);
var returnValue = Assert.IsType<ArtistaForm>(okResult.Value);
Assert.Equal(expectedArtista.Id, returnValue.Id);
Assert.Equal(expectedArtista.nome, returnValue.Nome);
}
}
I tried setting up unit tests for my ASP.NET Core application using xUnit and Moq with an in-memory database. I expected the tests to correctly use the seeded data from the in-memory database and return the expected results. However, the results were always null. Specifically, I wanted the ArtistaController to retrieve an ArtistaModel object based on its ID, but despite the setup, the test did not return the expected data from the in-memory database.
Here is a whole working demo you could follow:
Services
public interface IArtistaService
{
Task<ArtistaForm> GetAsync(string id);
}
public interface IArtistaRepository : IGenericRepository<ArtistaModel>
{
Task<ArtistaModel> GetByIdAsync(string id);
}
public interface IGenericRepository<T> where T : class
{
Task AddAsync(T entity);
Task DeleteAsync(T entity);
Task UpdateAsync(T entity);
Task<IEnumerable<T>> GetAllAsync();
Task<T> GetByIdAsync(string id);
}
public class ArtistaService : IArtistaService
{
private readonly IArtistaRepository _artistaRepository;
public ArtistaService(IArtistaRepository artistaRepository)
{
_artistaRepository = artistaRepository;
}
public async Task<ArtistaForm> GetAsync(string id)
{
var artista = await _artistaRepository.GetByIdAsync(id);
if (artista == null) return null;
return new ArtistaForm { Id = artista.Id, Nome = artista.nome };
}
}
public class ArtistaRepository : GenericRepository<ArtistaModel>, IArtistaRepository
{
public ArtistaRepository(MvcProj8_0Context context) : base(context)
{
}
public async Task<ArtistaModel> GetByIdAsync(string id)
{
return await _dbSet.FindAsync(id);
}
}
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
protected readonly MvcProj8_0Context _context;
protected readonly DbSet<T> _dbSet;
public GenericRepository(MvcProj8_0Context context)
{
_context = context;
_dbSet = context.Set<T>();
}
public async Task AddAsync(T entity)
{
await _dbSet.AddAsync(entity);
await _context.SaveChangesAsync();
}
public async Task DeleteAsync(T entity)
{
_dbSet.Remove(entity);
await _context.SaveChangesAsync();
}
public async Task UpdateAsync(T entity)
{
_dbSet.Update(entity);
await _context.SaveChangesAsync();
}
public async Task<IEnumerable<T>> GetAllAsync()
{
return await _dbSet.ToListAsync();
}
public async Task<T> GetByIdAsync(string id)
{
return await _dbSet.FindAsync(id);
}
}
Controller
public class ArtistaController : ControllerBase
{
private readonly IArtistaService _artistaService;
public ArtistaController(IArtistaService artistaService)
{
_artistaService = artistaService;
}
[HttpGet("{id}")]
public async Task<ActionResult<ArtistaModel>> Get(string id)
{
var artista = await _artistaService.GetAsync(id);
if (artista == null)
{
return NotFound();
}
return Ok(artista);
}
}
DataBaseFixture
public class DataBaseFixture : IDisposable
{
public MvcProj8_0Context Context { get; private set; }
public DataBaseFixture()
{
var options = new DbContextOptionsBuilder<MvcProj8_0Context>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
Context = new MvcProj8_0Context(options);
SeedDatabase();
}
private void SeedDatabase()
{
Context.Artista.AddRange(
new ArtistaModel { Id = "1", nome = "Oda" },
new ArtistaModel { Id = "2", nome = "Echiro" },
new ArtistaModel { Id = "3", nome = "Echiro Oda" }
);
Context.SaveChanges();
}
public void Dispose()
{
Context.Database.EnsureDeleted();
Context.Dispose();
}
}
Test
public class ArtistaControllerTest : IClassFixture<DataBaseFixture>
{
private readonly DataBaseFixture _fixture;
private readonly ArtistaController _controller;
public ArtistaControllerTest(DataBaseFixture fixture)
{
_fixture = fixture;
// Setup the repository and service using the in-memory context
var artistaRepository = new ArtistaRepository(_fixture.Context);
var artistaService = new ArtistaService(artistaRepository);
// Initialize the controller with the actual service
_controller = new ArtistaController(artistaService);
}
[Fact]
public async Task GetArtista()
{
var artistaId = "1";
var expectedArtista = new ArtistaForm { Id = artistaId, Nome = "Oda" };
var result = await _controller.Get(artistaId);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result.Result);
var returnValue = Assert.IsType<ArtistaForm>(okResult.Value);
Assert.Equal(expectedArtista.Id, returnValue.Id);
Assert.Equal(expectedArtista.Nome, returnValue.Nome);
}
}