c# - Mocking Extension Methods con Moq
unit-testing extension-methods (4)
Tengo una interfaz preexistente ...
public interface ISomeInterface
{
void SomeMethod();
}
y he extendido esta interfaz usando un mixin ...
public static class SomeInterfaceExtensions
{
public static void AnotherMethod(this ISomeInterface someInterface)
{
// Implementation here
}
}
Tengo una clase llamada eso que quiero probar ...
public class Caller
{
private readonly ISomeInterface someInterface;
public Caller(ISomeInterface someInterface)
{
this.someInterface = someInterface;
}
public void Main()
{
someInterface.AnotherMethod();
}
}
y una prueba en la que me gustaría burlarme de la interfaz y verificar la llamada al método de extensión ...
[Test]
public void Main_BasicCall_CallsAnotherMethod()
{
// Arrange
var someInterfaceMock = new Mock<ISomeInterface>();
someInterfaceMock.Setup(x => x.AnotherMethod()).Verifiable();
var caller = new Caller(someInterfaceMock.Object);
// Act
caller.Main();
// Assert
someInterfaceMock.Verify();
}
Sin embargo, ejecutar esta prueba genera una excepción ...
System.ArgumentException: Invalid setup on a non-member method:
x => x.AnotherMethod()
Mi pregunta es, ¿hay una buena manera de burlarse de la llamada mixin?
Descubrí que tenía que descubrir el interior del método de extensión para el que intentaba burlar la entrada y simular lo que sucedía dentro de la extensión.
Vi usando una extensión como agregar código directamente a su método. Esto significaba que necesitaba burlarme de lo que sucede dentro de la extensión en lugar de la extensión misma.
He usado una envoltura para solucionar este problema. Crea un objeto envoltorio y pasa tu método burlado.
Ver Mocking Static Methods for Unit Testing por Paul Irwin, tiene buenos ejemplos.
Me gusta utilizar el contenedor (patrón de adaptador) cuando estoy envolviendo el objeto en sí. No estoy seguro de que use eso para envolver un método de extensión, que no es parte del objeto.
Utilizo una propiedad Inyectable Lazy interna de cualquiera de los tipos Acción, Func, Predicado o delegado y me permito inyectar (intercambiar) el método durante una prueba unitaria.
internal Func<IMyObject, string, object> DoWorkMethod
{
[ExcludeFromCodeCoverage]
get { return _DoWorkMethod ?? (_DoWorkMethod = (obj, val) => { return obj.DoWork(val); }); }
set { _DoWorkMethod = value; }
} private Func<IMyObject, string, object> _DoWorkMethod;
Luego llama al Func en lugar del método real.
public object SomeFunction()
{
var val = "doesn''t matter for this example";
return DoWorkMethod.Invoke(MyObjectProperty, val);
}
Para obtener un ejemplo más completo, consulte http://www.rhyous.com/2016/08/11/unit-testing-calls-to-complex-extension-methods/
No se puede simular "directamente" el método estático (por lo tanto, el método de extensión) con el marco de burla. Puede probar Moles ( http://research.microsoft.com/en-us/projects/pex/downloads.aspx ), una herramienta gratuita de Microsoft que implementa un enfoque diferente. Aquí está la descripción de la herramienta:
Moles es un marco ligero para pruebas y desvíos en .NET que se basa en delegados.
Los moles se pueden utilizar para desviar cualquier método .NET, incluidos los métodos no virtuales / estáticos en tipos sellados.
Puede usar Moles con cualquier marco de prueba (es independiente de eso).