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 forma 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 una manera diferente?
A continuación se muestra un ejemplo trivial que falla con la "expectativa no válida en un miembro no reemplazable" habitual . Este es un mal ejemplo de la necesidad de simular 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 los adictos a TypeMock que podrían sugerir que use Isolator en su lugar: agradezco el esfuerzo ya que parece que TypeMock podría hacer el trabajo con los ojos vendados y ebrio, pero nuestro presupuesto no aumentará pronto.
fuente
Respuestas:
Los métodos de extensión son solo métodos estáticos disfrazados. Los frameworks simulados como Moq o Rhinomocks solo pueden crear instancias simuladas de objetos, esto significa que no es posible simular métodos estáticos.
fuente
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 un envoltorio 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 simular 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)); } }
fuente
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:
fuente
Creé una clase contenedora para los métodos de extensión que necesitaba simular.
public static class MyExtensions { public static string MyExtension<T>(this T obj) { return "Hello World!"; } } public interface IExtensionMethodsWrapper { string MyExtension<T>(T myObj); } public class ExtensionMethodsWrapper : IExtensionMethodsWrapper { public string MyExtension<T>(T myObj) { return myObj.MyExtension(); } }
Luego, puede simular los métodos de envoltura en sus pruebas y codificar con su contenedor IOC.
fuente
Para los métodos de extensión, normalmente uso el siguiente enfoque:
public static class MyExtensions { public static Func<int,int, int> _doSumm = (x, y) => x + y; public static int Summ(this int x, int y) { return _doSumm(x, y); } }
Permite inyectar _doSumm bastante fácil.
fuente
Lo mejor que puede hacer es proporcionar una implementación personalizada para el tipo que tiene el método de extensión, por ejemplo:
[Fact] public class Tests { public void ShouldRunOk() { var service = new MyService(new FakeWebHostEnvironment()); // Service.DoStuff() internally calls the SomeExtensionFunction() on IWebHostEnvironment // Here it works just fine as we provide a custom implementation of that interface service.DoStuff().Should().NotBeNull(); } } public class FakeWebHostEnvironment : IWebHostEnvironment { /* IWebHostEnvironment implementation */ public bool SomeExtensionFunction() { return false; } }
fuente