¿Por qué recibo una excepción con el mensaje "Configuración no válida en un miembro no virtual (reemplazable en VB) ..."?

176

Tengo una prueba unitaria donde tengo que burlarme de un método no virtual que devuelve un tipo bool

public class XmlCupboardAccess
{
    public bool IsDataEntityInXmlCupboard(string dataId,
                                          out string nameInCupboard,
                                          out string refTypeInCupboard,
                                          string nameTemplate = null)
    {
        return IsDataEntityInXmlCupboard(_theDb, dataId, out nameInCupboard, out refTypeInCupboard, nameTemplate);
    }
}

Así que tengo un objeto simulado de XmlCupboardAccessclase y estoy tratando de configurar simulacro para este método en mi caso de prueba como se muestra a continuación

[TestMethod]
Public void Test()
{
    private string temp1;
    private string temp2;
    private Mock<XmlCupboardAccess> _xmlCupboardAccess = new Mock<XmlCupboardAccess>();
    _xmlCupboardAccess.Setup(x => x.IsDataEntityInXmlCupboard(It.IsAny<string>(), out temp1, out temp2, It.IsAny<string>())).Returns(false); 
    //exception is thrown by this line of code
}

Pero esta línea arroja una excepción.

Invalid setup on a non-virtual (overridable in VB) member: 
x => x.IsDataEntityInXmlCupboard(It.IsAny<String>(), .temp1, .temp2, 
It.IsAny<String>())

¿Alguna sugerencia de cómo evitar esta excepción?

Rahul Lodha
fuente
¿De qué depende tu examen XmlCupboardAccess?
Preston Guillot
9
es simple ... necesitas marcarlo virtual. Moq no puede burlarse de un tipo concreto que no puede anular.
Simon Whitehead

Respuestas:

265

Moq no puede burlarse de métodos no virtuales y clases selladas. Mientras ejecuta una prueba usando un objeto simulado, MOQ realmente crea un tipo de proxy en memoria que hereda de su "XmlCupboardAccess" y anula los comportamientos que ha configurado en el método "SetUp". Y como sabe en C #, puede anular algo solo si está marcado como virtual, que no es el caso con Java. Java asume que cada método no estático es virtual por defecto.

Otra cosa que creo que deberías considerar es introducir una interfaz para tu "CupboardAccess" y comenzar a burlarte de la interfaz. Le ayudaría a desacoplar su código y obtener beneficios a largo plazo.

Por último, hay marcos como: TypeMock y JustMock que funcionan directamente con el IL y, por lo tanto, pueden burlarse de métodos no virtuales. Sin embargo, ambos son productos comerciales.

Amol
fuente
59
+1 en el hecho de que solo debes burlarte de las interfaces. Esta pregunta resolvió con qué me encontraba, porque accidentalmente me burlé de la clase y no de la interfaz subyacente.
Paul Raff
1
Esto no solo resuelve el problema, sino que es una buena práctica usar interfaces para todas sus clases que necesitan pruebas. Moq esencialmente te obliga a tener una buena inversión de dependencia, ya que algunos de los otros marcos de burla te permiten evitar este principio.
Xipooo
¿Se consideraría una violación de este principio si tengo una implementación falsa, por ejemplo, FakePeopleRepository, de mi interfaz, por ejemplo, IPeopleRepository, y me estoy burlando de la implementación falsa? Creo que IoC aún se conserva porque en mi configuración de prueba tengo que pasar el objeto falso a mi clase de servicio que toma la interfaz en su constructor.
paz
1
@paz El objetivo de usar MOQ es evitar la implementación falsa. Ahora considere cuántas variantes de la implementación falsa necesitaría para verificar las condiciones límite, etc. En teoría, sí, podría burlarse de una implementación falsa. Pero prácticamente suena como un olor a código.
Amol
Tenga en cuenta que este error puede ocurrir realmente con los métodos de extensión en las interfaces, lo que puede ser confuso.
Dan Pantry
34

Como ayuda para cualquiera que tuviera el mismo problema que yo, accidentalmente escribí mal el tipo de implementación en lugar de la interfaz, por ejemplo

var mockFileBrowser = new Mock<FileBrowser>();

en vez de

var mockFileBrowser = new Mock<IFileBrowser>();
Ralt
fuente
5

Por favor, vea ¿ Por qué la propiedad que quiero burlar debe ser virtual?

Es posible que deba escribir una interfaz de contenedor o marcar la propiedad como virtual / abstracta ya que Moq crea una clase proxy que utiliza para interceptar llamadas y devolver los valores personalizados que puso en la .Returns(x)llamada.

Bryida
fuente
5

En lugar de burlarse de la clase concreta, debe burlarse de esa interfaz de clase. Extraer interfaz de la clase XmlCupboardAccess

public interface IXmlCupboardAccess
{
    bool IsDataEntityInXmlCupboard(string dataId, out string nameInCupboard, out string refTypeInCupboard, string nameTemplate = null);
}

Y en lugar de

private Mock<XmlCupboardAccess> _xmlCupboardAccess = new Mock<XmlCupboardAccess>();

cambiar a

private Mock<IXmlCupboardAccess> _xmlCupboardAccess = new Mock<IXmlCupboardAccess>();
Sashus
fuente
3

También obtendrá este error si está verificando que se llama a un método de extensión de una interfaz.

Por ejemplo, si te estás burlando:

var mockValidator = new Mock<IValidator<Foo>>();
mockValidator
  .Verify(validator => validator.ValidateAndThrow(foo, null));

Obtendrá la misma excepción porque .ValidateAndThrow()es una extensión en la IValidator<T>interfaz.

public static void ValidateAndThrow<T>(this IValidator<T> validator, T instance, string ruleSet = null)...

Scotty.NET
fuente
-12

Código:

private static void RegisterServices(IKernel kernel)
{
    Mock<IProductRepository> mock=new Mock<IProductRepository>();
    mock.Setup(x => x.Products).Returns(new List<Product>
    {
        new Product {Name = "Football", Price = 23},
        new Product {Name = "Surf board", Price = 179},
        new Product {Name = "Running shose", Price = 95}
    });

    kernel.Bind<IProductRepository>().ToConstant(mock.Object);
}        

Pero ver excepción.

Borat
fuente
44
¿Podría dar una explicación de su solución? Además, "ver excepción ..." queda pendiente. ¿Puedes ampliar esto?
amadan