es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Problema de burla de delegado con Moq en .Net Core 3.1

Estoy teniendo problemas al hacer una simulación de un delegado que devuelve un IHttpClient. Esto es requerido por el cliente y no puedo cambiar la implementación.

El código se ve algo así:

Delegado:

namespace Foo.Http
{
    public delegate IHttpClient FooHttpClientProvider(string name);
}

Clase de prueba:

[SetUp]
public void SetUp()
{
    Mock<foohttpclientprovider>().Setup(x => x("HttpClient")).Returns(Mock<ihttpclient>().Object);
}

Cuando hago esto, obtengo la siguiente excepción:

System.InvalidCastException: No se puede convertir el objeto del tipo ‘Foo.Http.FooHttpClientProvider’ al tipo ‘Moq.IMocked`1[Foo.Http.FooHttpClientProvider]’.

¿Algún idea? ¿Es esto siquiera posible?

Editar: Algunos datos adicionales: el servicio que necesito probar recibe un FooHttpClientProvider inyectado. Usamos un Mock global con Autofac que registra los servicios que necesitamos. Dado que no puedo pasar el proveedor como parámetro, no puedo crear un nuevo Mock para FooHttpClientProvider y pasarlo a mi servicio como parámetro; necesito configurarlo en el mock global. Esto complica aún más las cosas.

El código del servicio se ve algo así:

private readonly FooHttpClientProvider _client;

public FooService(FooHttpClientProvider fooClient)
{
    _client = fooClient("HttpClient");
}

public async Task<outputdto> GetAsync(Request request)
{
    var accessToken = await _AuthService.GetAccessTokenAsync();

    try
    {
        var response = await ExecuteGet<outputdto>($"{FooConfig.BaseUrl}someEntpoint{GetQueryParams(request)}", accessToken);
        return response;
    }
    catch(Exception ex)
    {
        // Manejo de excepciones
    }
}

El servicio en sí funciona correctamente. El problema surge solo al intentar hacer una simulación (mock) para pruebas unitarias.

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Parece que el problema está en el tipo del objeto que estás simulando. El método Mock<t>() crea un objeto Moq.IMocked<t>, que es diferente al tipo FooHttpClientProvider.

    En lugar de simular directamente el delegado, podrías crear una instancia de FooHttpClientProvider y pasarla a tu servicio como parámetro. Luego, en la configuración de tu prueba, puedes crear una simulación de IHttpClient y pasarlo a la instancia de FooHttpClientProvider.

    Aquí tienes un ejemplo:

    [TestFixture]
    public class FooServiceTests
    {
        [Test]
        public async Task FooService_GetAsync_RetornaDtoDeSalida()
        {
            // Arrange
            var mockHttpClient = new Mock<ihttpclient>();
            var fooClientProvider = CreateFooHttpClientProvider(mockHttpClient.Object);
            var fooService = new FooService(fooClientProvider);
    
            // Act
            var outputDto = await fooService.GetAsync(new Request());
    
            // Assert
            Assert.IsNotNull(outputDto);
        }
    
        private FooHttpClientProvider CreateFooHttpClientProvider(IHttpClient httpClient)
        {
            return (name) => {
                if (name == "HttpClient")
                {
                    return httpClient;
                }
    
                throw new ArgumentException("Nombre de cliente HTTP inesperado: " + name);
            };
        }
    }
    

    En este ejemplo, creamos una simulación de IHttpClient y la pasamos a CreateFooHttpClientProvider(), que devuelve una instancia de FooHttpClientProvider. Esta instancia devolverá la simulación de IHttpClient cuando se le pase el nombre “HttpClient”.

    Luego, pasamos la instancia de FooHttpClientProvider al constructor de FooService, que la utilizará para obtener la instancia de IHttpClient para realizar las solicitudes HTTP. Por último, llamamos al método GetAsync() y aseguramos que devuelve un OutputDto no nulo.

    Esto debería permitirte simular el delegado y probar tu servicio sin cambiar la implementación.

Comments are closed.