web-dev-qa-db-fra.com

Inject Mock pour les objets créés par les classes Factory

J'ai la classe suivante:

public class MyClass {        
    private Apple apple;

    public void myMethod() {
       Apple = AppleFactory.createInstance(someStringVariable);
       ....
       ....
       ....
    }
}

Et la classe de test:

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

        @InjectMocks 
        MyClass myClass;

        @Test
        public void myMethod(){
         ...
         ...
         ...
        }
    }

Comment puis-je injecter une instance Apple comme maquette dans MyClass?

14
saravana_pc

Vous avez 3 possibilités pour résoudre ceci:

Fabrique abstraite : Au lieu d’utiliser une méthode statique, utilisez une classe d’usine concrète:

public abstract class AppleFactory {
    public Apple createInstance(final String str);
}

public class AppleFactoryImpl implements AppleFactory {
    public Apple createInstance(final String str) { // Implementation }
}

Dans votre classe de test, moquez-vous de l'usine:

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Mock
    private AppleFactory appleFactoryMock;

    @Mock
    private Apple appleMock;

    @InjectMocks 
    MyClass myClass;

    @Before
    public void setup() {
        when(appleFactoryMock.createInstance(Matchers.anyString()).thenReturn(appleMock);
    }

    @Test
    public void myMethod(){
     ...
     ...
     ...
    }
}

PowerMock : Utilisez PowerMock pour créer une maquette d'une méthode statique. Regardez ma réponse à une question pertinente pour voir comment cela se fait.

Classe testable : Créez la création Apple encapsulée dans une méthode protected et créez une classe de test qui la redéfinit:

public class MyClass {
   private Apple apple;

   public void myMethod() {
       Apple = createApple();
       ....
       ....
       ....
   }

   protected Apple createApple() {
       return AppleFactory.createInstance(someStringVariable);
   }
}


@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Mock
    private Apple appleMock;

    @InjectMocks 
    MyClass myClass;

    @Test
    public void myMethod(){
     ...
     ...
     ...
    }

    private class TestableMyClass extends MyClass {
       @Override
       public void createApple() {
          return appleMock;
       }
    }
}

Bien sûr, dans votre classe de test, vous devriez tester TestableMyClass et non MyClass.

Je vais vous dire mon opinion sur chacune des méthodes:

  1. La méthode de fabrique abstraite est la meilleure - C’est une conception claire qui cache les détails de la mise en oeuvre

  2. La classe testable - est la deuxième option qui nécessite des modifications minimales

  3. L'option PowerMock est ma moins préférée - au lieu de chercher un meilleur design, vous ignorez et cachez votre problème. Mais c'est toujours une option valable.
19
Avi

En ce qui concerne la première réponse de Avi & Ev0oD. Les classes abstraites peuvent uniquement être étendues et non implémentées.

  public abstract class AppleFactory {
    public abstract Apple createInstance(final String str);
}

public class AppleFactoryImpl extends AppleFactory {
    public Apple createInstance(final String str) { // Implementation }
}
2
ugurkocak1980

En plus de la solution proposée par Avi, vous pouvez choisir une quatrième possibilité:

Injecter en usine: Ceci est, pour moi, la meilleure option lorsque vous avez déjà du code à refacrot. Avec cette solution, vous n'avez pas besoin de changer le code de réduction, mais seulement la classe d'usine et le test.

public class AppleFactory
{
    private static Apple _injectedApple;

    public static createInstance(String str)
    {
        if (_injectedApple != null)
        {
            var currentApple = _injectedApple;
            _injectedApple = null;
            return currentApple;
        }

        //standard implementation
    }

    public static setInjectedApple(Apple apple)
    {
        _injectedApple = Apple;
    }
}

Maintenant, vous pouvez utiliser votre usine statique simplement:

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Mock
    private Apple appleMock;

    @InjectMocks 
    MyClass myClass;

    @Before
    public void setup() {
        AppleFactory.setInjectedApple(appleMock);
    }

    @Test
    public void myMethod(){
     ...
     ...
     ...
    }
}
0
Glauco Cucchiar