Creé un proyecto xUnit para probar este código de muestra
public class ClassToTest { private readonly ILogger<ClassToTest> _logger; public ClassToTest(ILogger<ClassToTest> logger) { _logger = logger; } public void Foo() => _logger.LogError(string.Empty); }Instalé Moq para crear una instancia simulada para el registrador
public class ClassToTestTests { private readonly ClassToTest _classToTest; private readonly Mock<ILogger<ClassToTest>> _loggerMock; public ClassToTestTests() { _loggerMock = new Mock<ILogger<ClassToTest>>(); _classToTest = new ClassToTest(_loggerMock.Object); } [Fact] public void TestFoo() { _classToTest.Foo(); _loggerMock.Verify(logger => logger.LogError(It.IsAny<string>()), Times.Once); } }Al ejecutar las pruebas me sale este mensaje de error
System.NotSupportedException: expresión no admitida: logger => logger.LogError(It.IsAny(), new[] { })
System.NotSupportedException Expresión no admitida: logger => logger.LogError(It.IsAny(), new[] { }) Los métodos de extensión (aquí: LoggerExtensions.LogError) no se pueden usar en expresiones de configuración/verificación.
Después de algunas investigaciones, sé que todos los métodos de registro son solo métodos de extensión. Moq no puede configurar métodos de extensión.
Me gustaría evitar la instalación de paquetes de terceros adicionales para este problema. ¿Hay alguna solución para pasar la prueba?
No puedes burlarte de los métodos de extensión.
en lugar de burlarse
logger.LogError(...)Necesitas burlarte del método de interfaz.
void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter);LogError en realidad llama a ese método de interfaz así
logger.Log(LogLevel.Error, 0, new FormattedLogValues(message, args), null, (state, ex) => state.ToString());Así que tienes que burlarte
_loggerMock.Verify(logger => logger.Log(It.Is(LogLevel.Error), It.Is(0), It.IsAny<FormattedLogValues>(), It.IsAny<Exception>(), It.IsAny<Func<TState, Exception, string>>()), Times.Once);Descargo de responsabilidad No verifiqué el código
Editar después del comentario de pinkfloydx33, configuré un ejemplo de prueba en .net50 y llegué a la siguiente respuesta
Con el marco más reciente, la clase FormattedLogValues se ha hecho interna. Por lo tanto, no puede usar esto con los miembros genéricos de Moq.It. Pero Moq tiene una forma alternativa de hacer esto (esta respuesta también mencionó la solución)
Para una llamada al registrador como este
_logger.LogError("myMessage");Necesitas verificar así
_loggerMock.Verify(logger => logger.Log( It.Is<LogLevel>(logLevel => logLevel == LogLevel.Error), It.Is<EventId>(eventId => eventId.Id == 0), It.Is<It.IsAnyType>((@object, @type) => @object.ToString() == "myMessage" && @type.Name == "FormattedLogValues"), It.IsAny<Exception>(), It.IsAny<Func<It.IsAnyType, Exception, string>>()), Times.Once);Utiliza It.IsAnyType para los tipos a los que no tiene acceso. Y si necesita restringir la verificación, puede agregar una función <objeto, tipo> para verificar si el tipo es el que espera o enviarlo a la interfaz pública y validar cualquier miembro público que tenga.
Cuando trabaja con un mensaje de cadena y algunos parámetros, debe convertir el objeto de tipo FormattedLogValues en la interfaz IReadOnlyList<KeyValuePair<string, object?>> y verificar la cadena o los valores de los diferentes parámetros.