web-dev-qa-db-fra.com

Pourquoi ai-je une exception avec le message "Configuration non valide sur un membre non virtuel (pouvant être remplacé par VB) ..."?

J'ai un test unitaire où je dois me moquer d'une méthode non virtuelle qui renvoie un type 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);
    }
}

J'ai donc un objet fictif de classe XmlCupboardAccess et j'essaie de configurer fictif pour cette méthode dans mon scénario de test, comme indiqué ci-dessous

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

Mais cette ligne jette exception 

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

Des suggestions pour contourner cette exception?

135
Rahul Lodha

Moq ne peut pas se moquer des méthodes non virtuelles et des classes scellées. Lors de l'exécution d'un test à l'aide d'un objet fictif, MOQ crée un type de proxy en mémoire qui hérite de votre "XmlCupboardAccess" et remplace les comportements que vous avez définis dans la méthode "SetUp". Et comme vous le savez en C #, vous ne pouvez remplacer un élément que s'il est marqué comme virtuel, ce qui n'est pas le cas avec Java. Java suppose que chaque méthode non statique est virtuelle par défaut.

Une autre chose que vous devriez envisager est d’introduire une interface pour votre "CupboardAccess" et de commencer à vous moquer de l’interface. Cela vous aiderait à découpler votre code et aurait des avantages à long terme.

Enfin, il existe des cadres tels que: TypeMock et JustMock qui travaillent directement avec l’IL et peuvent donc se moquer des méthodes non virtuelles. Cependant, les deux sont des produits commerciaux.

223
Amol

Pour aider tous ceux qui rencontraient le même problème que moi, j’ai mal orthographié le type de mise en œuvre au lieu de l’interface, par exemple.

var mockFileBrowser = new Mock<FileBrowser>();

au lieu de

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

S'il vous plaît voir Pourquoi la propriété que je veux simuler doit-elle être virtuelle?

Vous devrez peut-être écrire une interface wrapper ou marquer la propriété comme étant virtuelle/abstraite, car Moq crée une classe proxy utilisée pour intercepter les appels et renvoyer les valeurs personnalisées que vous avez définies dans l'appel .Returns(x).

4
Bryida

Au lieu de vous moquer d'une classe concrète, vous devriez vous moquer de cette interface de classe . Extraire l'interface de la classe XmlCupboardAccess 

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

Et au lieu de

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

changer à 

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