Solucionado (ver solução)
Solucionado
(ver solução)
2
respostas

[Dúvida] Como realizar Teste com Xunit e Mock com banco em Memoria?

Em asp.net core 6 estou utilizando mariadb e tenho uma Controller que em seu construtor faz injecao de dependencia da IService, na sua Service faz Injecao para sua IRepository e sua Repository herda de uma GenericRepository que ai sim contem a injecao de DBcontext. eu quero utilizar um banco em memoria durante o teste para isso crie DataBaseFixture : IDisposable que ja popula tambem alguns dados, porem a minha dificuldade esta em como implementar os testes e informar que é para utilizar o Context de DataBaseFixture.

ArtistaController.cs

 [Route("api/[controller]")]
 public class ArtistaController : ControllerBase
 {
     private readonly IArtistaService _artistaService;

     public ArtistaController(IArtistaService context)
     {
         _artistaService = context;
     }
...

ArtistaService:

public class ArtistaService : IArtistaService
{

    private readonly IArtistaRepository _artistaRepository;
    private readonly IContextRepository _contextRepository;

    public ArtistaService(IArtistaRepository artistaRepository, IContextRepository contextRepository)
    {
        _artistaRepository = artistaRepository;
        _contextRepository = contextRepository;
    }

...

ArtistaRepository:

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

...

o result esta sempre me retornando null ja tentei diversas formas 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);

    }
}
2 respostas
solução!

Oi Lucas! Tudo bem?

Vamos ver como podemos ajustar seu código para garantir que o DataBaseFixture seja corretamente utilizado durante os testes.

Primeiro, certifique-se de que o DataBaseFixture está configurando corretamente o contexto e populando os dados. Em seguida, vamos ajustar o ArtistaControllerTest para utilizar o contexto de DataBaseFixture corretamente.

Aqui está uma abordagem que pode ajudar:

  1. Configuração do DataBaseFixture: certifique-se de que o SeedDatabase está sendo chamado corretamente e que os dados estão sendo salvos no contexto.

    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.Dispose();
        }
    }
    
  2. Ajuste no ArtistaControllerTest: certifique-se de que o mock do serviço está configurado corretamente para retornar os dados esperados.

    public class ArtistaControllerTest : IClassFixture<DataBaseFixture>
    {
        private readonly DataBaseFixture _fixture;
        private readonly ArtistaController _controller;
    
        public ArtistaControllerTest(DataBaseFixture fixture)
        {
            _fixture = fixture;
    
            var 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);
                if (artista == null) return null;
                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);
        }
    }
    

No mais, tenha certeza de que o método Get no ArtistaController está retornando um OkObjectResult com o tipo ArtistaForm.

Espero ter ajudado e bons estudos!

Caso este post tenha lhe ajudado, por favor, marcar como solucionado ✓.

Oi, depois de um tempo programando um pouco mais acabei encontrando uma outra solução para o codigo utlizando o Mock.

Configuração do DataBaseFixture

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.Dispose();
    }
}

Ajuste no ArtistaControllerTest: implementar as classes da injeção de dependência da service passando o context nela e fazer o mock da service:

 public class ArtistaControllerTest : IClassFixture<DataBaseFixture>
 {
     private readonly DataBaseFixture _fixture;
     private readonly ArtistaController _controller;
     public IArtistaRepository ArtistaRepository { get; private set; }
     public IContextRepository ContextRepository { get; private set; }

     public ArtistaControllerTest(DataBaseFixture fixture)
     {
         _fixture = fixture;

         // Setup the repository and service using the in-memory context
         ArtistaRepository = new ArtistaRepository(fixture.Context);
         ContextRepository = new ContextRepository(fixture.Context);
         var ArtistaService = new Mock<ArtistaService>(ArtistaRepository, ContextRepository);

         // Initialize the controller with the actual service
         _controller = new ArtistaController(ArtistaService.Object);

     }

     [Fact]
     public async void 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);

     }
 }

Com isso você não precisa realizar um setup para cada método que for testar.