¿Cómo uso Moq para simular un método de extensión?
Estoy escribiendo una prueba que depende de los resultados de un método de extensión, pero no quiero que una falla futura de ese método de extensión rompa esta prueba. Burlarse de ese resultado parecía la opción obvia, pero Moq no parece ofrecer una manera de anular un método estático (un requisito para un método de extensión). Existe una idea similar con Moq.Protected y Moq.Stub, pero no parecen ofrecer nada para este escenario. ¿Me estoy perdiendo algo o debería hacerlo de otra manera?
A continuación se muestra un ejemplo trivial que falla con la habitual "expectativa no válida en un miembro no anulable" . Este es un mal ejemplo de la necesidad de burlarse de un método de extensión, pero debería funcionar.
public class SomeType {
int Id { get; set; }
}
var ListMock = new Mock<List<SomeType>>();
ListMock.Expect(l => l.FirstOrDefault(st => st.Id == 5))
.Returns(new SomeType { Id = 5 });
En cuanto a cualquier adicto a TypeMock que pueda sugerirme que use Isolator en su lugar: aprecio el esfuerzo ya que parece que TypeMock podría hacer el trabajo con los ojos vendados y ebrio, pero nuestro presupuesto no aumentará en el corto plazo.
Los métodos de extensión son sólo métodos estáticos disfrazados. Los marcos simulados como Moq o Rhinomocks solo pueden crear instancias simuladas de objetos, esto significa que no es posible burlarse de métodos estáticos.
Si puede cambiar el código de los métodos de extensión, puede codificarlo así para poder probar:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
public static class MyExtensions
{
public static IMyImplementation Implementation = new MyImplementation();
public static string MyMethod(this object obj)
{
return Implementation.MyMethod(obj);
}
}
public interface IMyImplementation
{
string MyMethod(object obj);
}
public class MyImplementation : IMyImplementation
{
public string MyMethod(object obj)
{
return "Hello World!";
}
}
Entonces, los métodos de extensión son solo una envoltura alrededor de la interfaz de implementación.
(Podría usar solo la clase de implementación sin métodos de extensión que son una especie de azúcar sintáctico).
Y puede burlarse de la interfaz de implementación y configurarla como implementación para la clase de extensiones.
public class MyClassUsingExtensions
{
public string ReturnStringForObject(object obj)
{
return obj.MyMethod();
}
}
[TestClass]
public class MyTests
{
[TestMethod]
public void MyTest()
{
// Given:
//-------
var mockMyImplementation = new Mock<IMyImplementation>();
MyExtensions.Implementation = mockMyImplementation.Object;
var myClassUsingExtensions = new MyClassUsingExtensions();
// When:
//-------
var myObject = new Object();
myClassUsingExtensions.ReturnStringForObject(myObject);
//Then:
//-------
// This would fail because you cannot test for the extension method
//mockMyImplementation.Verify(m => m.MyMethod());
// This is success because you test for the mocked implementation interface
mockMyImplementation.Verify(m => m.MyMethod(myObject));
}
}
Sé que esta pregunta no ha estado activa durante aproximadamente un año, pero Microsoft lanzó un marco para manejar exactamente esto llamado Moles .
Aquí también hay algunos tutoriales: