Perché ricevo un'eccezione con il messaggio "Installazione non valida su un membro non virtuale (sostituibile in VB) ..."?


176

Ho un test unitario in cui devo prendere in giro un metodo non virtuale che restituisce 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);
    }
}

Quindi ho un oggetto finto di XmlCupboardAccessclasse e sto provando a impostare il finto per questo metodo nel mio caso di test, come mostrato di seguito

[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
}

Ma questa linea genera un'eccezione

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

Qualche suggerimento su come aggirare questa eccezione?


Da cosa dipende il tuo test XmlCupboardAccess?
Preston Guillot,

9
è semplice .. è necessario contrassegnarlo virtual. Moq non può deridere un tipo concreto che non può ignorare.
Simon Whitehead,

Risposte:


265

Moq non può deridere metodi non virtuali e classi sigillate. Durante l'esecuzione di un test utilizzando l'oggetto simulato, MOQ crea effettivamente un tipo di proxy in memoria che eredita da "XmlCupboardAccess" e sovrascrive i comportamenti impostati nel metodo "SetUp". E come sai in C #, puoi sovrascrivere qualcosa solo se è contrassegnato come virtuale, il che non è il caso di Java. Java presuppone che ogni metodo non statico sia virtuale per impostazione predefinita.

Un'altra cosa che credo dovresti considerare è l'introduzione di un'interfaccia per il tuo "CupboardAccess" e iniziare invece a deridere l'interfaccia. Ti aiuterebbe a disaccoppiare il tuo codice e ad avere benefici a lungo termine.

Infine, ci sono framework come: TypeMock e JustMock che lavorano direttamente con IL e quindi possono deridere metodi non virtuali. Entrambi, tuttavia, sono prodotti commerciali.


59
+1 sul fatto che dovresti solo deridere le interfacce. Questa domanda ha risolto quello che stavo incontrando, perché ho deriso accidentalmente la classe e non l'interfaccia sottostante.
Paul Raff,

1
Questo non solo risolve il problema, ma è buona norma utilizzare le interfacce per tutte le classi che necessitano di test. Moq ti sta essenzialmente costringendo ad avere una buona inversione di dipendenza, dove alcuni degli altri quadri di derisione ti permettono di aggirare questo principio.
Xipooo

Sarebbe considerato una violazione di questo principio se avessi un'implementazione falsa, ad esempio FakePeopleRepository, della mia interfaccia, ad esempio IPeopleRepository, e sto deridendo l'implementazione falsa? Penso che l'IoC sia ancora preservato perché nella mia configurazione di test devo passare l'oggetto falso alla mia classe di servizio che prende l'interfaccia nel suo costruttore.
paz,

1
@paz Il punto centrale dell'utilizzo di MOQ è evitare l'implementazione falsa. Ora considera quante varianti dell'implementazione falsa dovresti controllare le condizioni al contorno ecc. In teoria, sì, potresti deridere un'implementazione falsa. Ma praticamente sembra un odore di codice.
Amol,

Si noti che questo errore potrebbe effettivamente verificarsi con i metodi di estensione sulle interfacce, il che potrebbe creare confusione.
Dan Pantry,

34

Come aiuto per chiunque abbia avuto lo stesso problema, ho accidentalmente sbagliato a digitare il tipo di implementazione invece dell'interfaccia es

var mockFileBrowser = new Mock<FileBrowser>();

invece di

var mockFileBrowser = new Mock<IFileBrowser>();


5

Invece di deridere la classe concreta, dovresti deridere quell'interfaccia della classe. Estrai l'interfaccia dalla classe XmlCupboardAccess

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

E invece di

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

cambia in

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

3

Riceverai questo errore anche se stai verificando che viene chiamato un metodo di estensione di un'interfaccia.

Ad esempio se stai deridendo:

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

Otterrai la stessa eccezione perché .ValidateAndThrow()è un'estensione IValidator<T>sull'interfaccia.

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


-12

Codice:

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);
}        

ma vedi eccezione.


4
Potresti fornire una spiegazione della tua soluzione? Inoltre, "vedi eccezione ..." viene lasciato sospeso. Puoi approfondire su questo?
amadan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.