web-dev-qa-db-fra.com

Comment se moquer de la méthode avec un objet codé dur?

Je travaille sur une application qui ont plusieurs couches. couche d'accès aux données pour récupérer et enregistrer les données de source de données, la logique métier à manipuler les données, l'interface utilisateur pour afficher les données à l'écran.

Je unité faisant également tester de la couche logique métier. La seule exigence est de tester le flux de la logique de la couche d'affaires. Donc, j'utilise cadre Moq pour se moquer de la couche d'accès aux données et test unitaire la couche logique métier avec MS Unit.

J'utilise la programmation d'interface pour rendre le découpler de conception autant que possible de manière à pouvoir faire des tests unitaires. couche d'accès aux données appel couche d'affaires à travers l'interface.

Je suis confronté à un problème quand je suis en train de tester une des méthodes de logique métier. Cette méthode fait un travail et créer un objet et de le transmettre à la couche d'accès aux données. Quand je suis en train de se moquer de cette méthode de la couche d'accès aux données, alors il ne peut pas se moquer avec succès.

Ici, je suis en train de créer un code de démonstration pour montrer mon problème.

Modèle:

public class Employee
{
    public string Name { get; set; }
}

Couche d'accès aux données:

public interface IDal
{
    string GetMessage(Employee emp);
}

public class Dal : IDal
{
    public string GetMessage(Employee emp)
    {
        // Doing some data source access work...

        return string.Format("Hello {0}", emp.Name);
    }
}

Couche logique métier:

public interface IBll
{
    string GetMessage();
}

public class Bll : IBll
{
    private readonly IDal _dal;

    public Bll(IDal dal)
    {
        _dal = dal;
    }

    public string GetMessage()
    {
        // Object creating inside business logic method.
        Employee emp = new Employee(); 

        string msg = _dal.GetMessage(emp);
        return msg;
    }
}

Test de l'unité:

[TestMethod]
    public void Is_GetMessage_Return_Proper_Result()
    {
        // Arrange.
        Employee emp = new Employee; // New object.

        Mock<IDal> mockDal = new Mock<IDal>();
        mockDal.Setup(d => d.GetMessage(emp)).Returns("Hello " + emp.Name);

        IBll bll = new Bll(mockDal.Object);

        // Act.

        // This will create another employee object inside the 
        // business logic method, which is different from the 
        // object which I have sent at the time of mocking.
        string msg = bll.GetMessage(); 

        // Assert.
        Assert.AreEqual("Hello arnab", msg);
    }

En cas de test unitaire au moment de moqueur J'envoie un objet employé, mais lors de l'appel de la méthode de la logique métier, il crée différents objets employés dans la méthode. Voilà pourquoi je ne peux pas se moquer de l'objet.

Dans ce cas, comment concevoir que je puisse résoudre le problème?

11
DeveloperArnab

Au lieu de créer un objet Employee à l'aide de new, votre classe Bll pourrait utiliser une classe EmployeeFactory pour cela, avec une méthode createInstance, qui est injecté à travers le constructeur:

 class EmployeeFactory : IEmployeeFactory
 {
       public Employee createInstance(){return new Employee();}
 }

Le constructeur doit prendre l'objet d'usine via une interface IEmployeeFactory, vous permet de remplacer la "vraie" usine, c'est facilement par une fabrique simulée.

public class Bll : IBll
{
    private readonly IDal _dal;
    private readonly IEmployeeFactory _employeeFactory;

    public Bll(IDal dal, IEmployeeFactory employeeFactory)
    {
        _dal = dal;
        _employeeFactory=employeeFactory;
    }

    public string GetMessage()
    {
        // Object creating inside business logic method
        // *** using a factory ***
        Employee emp = _employeeFactory.createObject(); 
        // ...
    }
    //...
}

La maquette peut fournir le test avec n'importe quel type d'objet Employee dont vous avez besoin pour votre test (par exemple, createInstance pourrait toujours renvoyer le même objet):

 class MockEmployeeFactory : IEmployeeFactory
 {
       private Employee _emp;

       public MockEmployeeFactory()
       {
          _emp = new Employee();
          // add any kind of special initializing here for testing purposes
       }

       public Employee createInstance()
       {
          // just for testing, return always the same object
          return _emp;
       }
 }

Maintenant, en utilisant ce moquette dans votre test devrait faire le tour.

12
Doc Brown

Je le traiterais comme une unité unique à tester.

Tant que vous contrôlez toutes les entrées à partir de laquelle l'objet Employee est créé, le fait qu'il soit créé dans l'objet testé ne devrait pas avoir d'importance. Vous avez juste besoin d'une méthode moqueuse pour retourner le résultat attendu si le contenu de l'argument correspond à l'attente.

Évidemment, cela signifie que vous devez fournir une logique personnalisée pour la méthode moqueuse. La logique avancée ne peut souvent pas être testée avec simplement "pour x retour y" des simulacres.

En fait, vous devez non Rendez-lui un objet différent dans les tests que dans la production, car si vous le faites, vous ne testez pas le code qui devrait le créer. Mais ce code fait partie intégrante du code de production et devrait donc être couvert par le boîtier de test.

4
Jan Hudec

C'est un échec de certains outils de test, vous devez toujours utiliser des interfaces et tout doit être créé d'une manière qui vous permet d'échanger l'objet basé sur l'interface pour une autre.

Cependant, il y a de meilleurs outils - Prenez Microsoft Fakes (a été appelé Moles) qui vous permet d'échanger n'importe quel objet, même statique et mondial. Il faut une approche plus basse de remplacement des objets afin que vous n'ayez pas à utiliser des interfaces partout tout en conservant la manière de rédiger des tests que vous êtes habitués.

0
gbjbaanb