web-dev-qa-db-fra.com

Comment se moquer d'une classe sans interface?

Je travaille sur .NET 4.0 en utilisant C # dans Windows 7.

Je veux tester la communication entre certaines méthodes utilisant mock. Le seul problème est que je veux le faire sans implémenter une interface. Est-ce possible? Je viens de lire un grand nombre de sujets et quelques tutoriels sur les objets fictifs, mais tous utilisaient pour simuler des interfaces, et non les classes. J'ai essayé d'utiliser les frameworks Rhino et Moq.

54

Marquez simplement la méthode que vous devez simuler comme virtual (et non privée). Ensuite, vous pourrez créer un faux pouvant remplacer la méthode.

Si tu utilises new Mock<Type> et que vous n’avez pas de constructeur sans paramètre, vous pouvez alors passer les paramètres comme arguments de l’appel ci-dessus car il faut un type de param Objects

55
Justin Pihony

La plupart des frameworks moqueurs (Moq et RhinoMocks inclus) génèrent des classes proxy en remplacement de votre classe mocked et surchargent les méthodes virtuelles avec le comportement que vous définissez. De ce fait, vous ne pouvez que simuler des interfaces ou des méthodes virtuelles sur des classes concrètes ou abstraites. De plus, si vous moquez une classe concrète, vous devez presque toujours fournir un constructeur sans paramètre afin que le framework moqueur sache comment instancier la classe.

Pourquoi l'aversion pour la création d'interfaces dans votre code?

18
matt

Avec MoQ, vous pouvez simuler des cours concrets:

var mocked = new Mock<MyConcreteClass>();

mais cela vous permet de remplacer virtual code (méthodes et propriétés).

14
Roy Dictus

Les frameworks moqueurs standard créent des classes proxy. C'est la raison pour laquelle ils sont techniquement limités aux interfaces et aux méthodes virtuelles.

Si vous souhaitez également vous moquer des méthodes "normales", vous avez besoin d'un outil qui fonctionne avec l'instrumentation au lieu de la génération de proxy. Par exemple. MS Moles et Typemock peuvent le faire. Mais le premier a une "API" horrible, et le second est commercial.

6
Thomas Weller

Je pense qu'il est préférable de créer une interface pour cette classe. Et créez un test unitaire en utilisant l'interface.

Si vous n'avez pas accès à cette classe, vous pouvez créer un adaptateur pour cette classe.

Par exemple:

public class RealClass
{
    int DoSomething(string input)
    {
        // real implementation here
    }
}

public interface IRealClassAdapter
{
    int DoSomething(string input);
}

public class RealClassAdapter : IRealClassAdapter
{
    readonly RealClass _realClass;

    public RealClassAdapter() => _realClass = new RealClass();

    int DoSomething(string input) => _realClass.DoSomething(input);
}

De cette façon, vous pouvez facilement créer une maquette pour votre classe en utilisant IRealClassAdapter.

Esperons que ça marche.

6
Anang Satria

Si vous ne pouvez pas modifier la classe en cours de test, la seule option que je puisse vous suggérer consiste à utiliser MS Fakes https://msdn.Microsoft.com/en-us/library/hh549175.aspx . Toutefois, MS Fakes ne fonctionne que dans quelques éditions de Visual Studio.

4
Rustem Zinnatullin

Si le pire venait à se dégrader, vous pouvez créer une paire interface/adaptateur. Vous changerez toutes les utilisations de ConcreteClass pour utiliser l'interface à la place, et passerez toujours l'adaptateur à la place de la classe concrète dans le code de production.

L'adaptateur implémente l'interface afin que le simulacre puisse également implémenter l'interface.

Il s'agit davantage d'un échafaudage que de simplement rendre une méthode virtuelle ou simplement d'ajouter une interface, mais si vous n'avez pas accès au source de la classe concrète, vous pouvez vous sortir d'une contrainte.

1
Tim Ottinger

J'ai été confronté à quelque chose de ce genre dans l'un des projets anciens et existants dans lesquels j'ai travaillé, qui ne contient aucune interface ou meilleure pratique, et il est trop difficile de les appliquer à nouveau ou de refactoriser le code en raison de la maturité de l'activité de projet. Dans mon projet UnitTest, je créais un wrapper sur les classes que je voulais simuler et cette interface implémentant le wrapper contenant toutes les méthodes nécessaires que je souhaitais configurer et utiliser, je peux maintenant simuler le wrapper à la place de la classe réelle.

Par exemple:

Service que vous voulez tester qui ne contient pas de méthodes virtuelles ou d'implémenter une interface

public class ServiceA{

public void A(){}

public String B(){}

}

Wrapper to moq

public class ServiceAWrapper : IServiceAWrapper{

public void A(){}

public String B(){}

}

L'interface de wrapper

public interface IServiceAWrapper{

void A();

String B();

}

Dans le test unitaire, vous pouvez maintenant simuler le wrapper:

    public void A_Run_ChangeStateOfX()
    {
    var moq = new Mock<IServiceAWrapper>();
    moq.Setup(...);
    }

Ce n'est peut-être pas la meilleure pratique, mais si les règles de votre projet vous y obligent, faites-le. Placez également tous vos wrappers dans votre projet de test unitaire ou dans votre projet Helper spécifié uniquement pour les tests unitaires afin de ne pas surcharger le projet avec des wrappers ou des adaptateurs inutiles.

Mise à jour: Cette réponse datant de plus d'un an, mais cette année, j'ai été confrontée à de nombreux scénarios similaires avec des solutions différentes. Par exemple, il est si facile d'utiliser Microsoft Fake Framework pour créer des simulacres, des faux et des talons et même tester des méthodes privées et protégées sans aucune interface. Vous pouvez lire: https://docs.Microsoft.com/en-us/visualstudio/test/isolating-code-under-test-with-Microsoft-fakes?view=vs-2017

1
Marzouk