Utilisation d'IoC pour les tests unitaires

97

Comment un conteneur IoC peut-il être utilisé pour les tests unitaires? Est-il utile de gérer les simulations dans une énorme solution (plus de 50 projets) en utilisant l'IoC? Des expériences? Des bibliothèques C # qui fonctionnent bien pour l'utiliser dans les tests unitaires?

crauscher
la source
7
@Mark Seemann serait trop humble pour le signaler, mais si cette question vous intéresse, vous devriez au moins être au courant de AutoFixture
Ruben Bartelink
1
Il y a une bonne discussion sur la relation entre DI et moquerie sur Vimeo par Miguel Castro: vimeo.com/68390510
GregC

Réponses:

131

De manière générale, un conteneur DI ne devrait pas être nécessaire pour les tests unitaires, car les tests unitaires consistent à séparer les responsabilités.

Considérez une classe qui utilise l'injection de constructeur

public MyClass(IMyDependency dep) { }

Dans toute votre application, il se peut qu'il y ait un énorme graphique de dépendances caché derrière IMyDependency, mais dans un test unitaire, vous l'aplatissez en un seul test double .

Vous pouvez utiliser des simulations dynamiques comme Moq ou RhinoMocks pour générer le Test Double, mais ce n'est pas obligatoire.

var dep = new Mock<IMyDependency>().Object;
var sut = new MyClass(dep);

Dans certains cas, un conteneur à simulation automatique peut être agréable à avoir, mais vous n'avez pas besoin d'utiliser le même conteneur DI que celui utilisé par l'application de production.

Mark Seemann
la source
13
d'accord ... à moins qu'une cible de test n'ait un conteneur IoC en tant que dépendance, vos tests ne devraient pas en avoir besoin ... vous allez supprimer la majorité du graphe d'objets lorsque vous effectuez vos tests unitaires.
Anderson Imes
4
@Mark Seemann Cela a du sens ... Mais qu'en est-il des tests d'intégration? C'est-à-dire que j'ai joué avec les tests d'interface utilisateur et j'ai été confronté à une situation où je devais partager la racine de composition. Des commentaires?
Arnis Lapsa
5
@Arnis L.: Pour les tests d'intégration, c'est moins important. Vous pouvez choisir d'utiliser un conteneur DI pour câbler les composants, mais si tel est le cas, vous aurez probablement besoin d'une configuration différente pour le conteneur que dans l'application complète - sauf si vous effectuez un test sous-cutané ou un test système complet, auquel cas vous pouvez réutiliser la configuration de l'application du conteneur.
Mark Seemann
ref to msdn magazine Test Double: download.microsoft.com/download/3/A/7
...
18

Comment un conteneur Ioc peut-il être utilisé pour les tests unitaires?

IoC appliquera des paradigmes de programmation qui faciliteront les tests unitaires isolés (c'est-à-dire en utilisant des simulations): utilisation d'interfaces, pas de new (), pas de singletons ...

Mais utiliser le conteneur IoC pour les tests n'est pas vraiment une exigence, il fournira juste quelques installations, par exemple l'injection de simulacres, mais vous pouvez le faire manuellement.

Est-il utile de gérer les simulations dans une énorme solution (plus de 50 projets) en utilisant l'IoC?

Je ne sais pas ce que vous entendez par la gestion des simulations à l'aide d'IoC. Quoi qu'il en soit, les conteneurs IoC peuvent généralement faire plus que simplement injecter des simulations lorsqu'il s'agit de tests. Et si vous disposez d'un support IDE décent qui rend le refactoring possible, pourquoi ne pas l'utiliser?

Une expérience?

Oui, sur une solution énorme, vous avez plus que jamais besoin d'une solution non sujette aux erreurs et défavorable au refactoring (c'est-à-dire via un conteneur IoC de type sécurisé ou un bon support IDE).

Pascal Thivent
la source
17

J'utilise souvent un conteneur IoC dans mes tests. Certes, ce ne sont pas des "tests unitaires" au sens pur. IMO Ils sont plus BDDish et facilitent la refactorisation. Les tests sont là pour vous donner confiance pour refactoriser. Des tests mal écrits peuvent être comme verser du ciment dans votre code.

Considérer ce qui suit:

[TestFixture]
public class ImageGalleryFixture : ContainerWiredFixture
{
    [Test]
    public void Should_save_image()
    {
        container.ConfigureMockFor<IFileRepository>()
            .Setup(r => r.Create(It.IsAny<IFile>()))
            .Verifiable();

        AddToGallery(new RequestWithRealFile());

        container.VerifyMockFor<IFileRepository>();
    }

    private void AddToGallery(AddBusinessImage request)
    {
        container.Resolve<BusinessPublisher>().Consume(request);
    }
}

Il y a plusieurs choses qui se produisent lors de l'ajout d'une image à la galerie. L'image est redimensionnée, une miniature est générée et les fichiers sont stockés sur AmazonS3. En utilisant un conteneur, je peux plus facilement isoler uniquement le comportement que je veux tester, qui dans ce cas est la partie persistante.

Une extension de conteneur auto-mocking est utile lorsque vous utilisez cette technique: http://www.agileatwork.com/auto-mocking-unity-container-extension/

Mike Valenty
la source
8
+1 pour l'expression "comme verser du ciment dans votre code". J'ai commencé à l'utiliser tout le temps.
Andrew Shepherd
2

En utilisant des conteneurs avec la capacité de résoudre des services non enregistrés / inconnus comme SimpleInjector , DryIoc (son mien) peut renvoyer des simulations pour des interfaces non encore implémentées.

Ce qui signifie que vous pouvez commencer le développement avec une première implémentation simple et ses dépendances simulées, et les remplacer par des éléments réels au fur et à mesure de votre progression.

papa
la source