webentwicklung-frage-antwort-db.com.de

Inject-Mocks für Objekte, die von Factory-Klassen erstellt wurden

Ich habe folgende Klasse:

public class MyClass {        
    private Apple apple;

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

Und die Testklasse:

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

        @InjectMocks 
        MyClass myClass;

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

Wie kann ich eine Apple-Instanz als Mock in MyClass einfügen?

14
saravana_pc

Sie haben 3 Möglichkeiten, dies zu lösen:

Abstract factory : Verwenden Sie statt einer statischen Methode eine konkrete Factory-Klasse:

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

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

Verspotten Sie in Ihrer Testklasse die Fabrik:

@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 : Verwenden Sie PowerMock, um einen Mock einer statischen Methode zu erstellen. Schauen Sie sich meine Antwort auf eine relevante Frage an, um zu sehen, wie es gemacht wird.

Testable class : Erstellen Sie die Apple-Erstellung in einer protected-Methode und erstellen Sie eine Testklasse, die sie überschreibt:

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

Natürlich sollten Sie in Ihrer Testklasse TestableMyClass und nicht MyClass testen.

Ich werde Ihnen meine Meinung zu jeder der Methoden mitteilen:

  1. Die abstrakte Factory-Methode ist die beste - Dies ist ein klares Design, das die Implementierungsdetails verbirgt

  2. Die überprüfbare Klasse - Ist die zweite Option, die minimale Änderungen erfordert

  3. Die Option PowerMock ist mein am wenigsten bevorzugtes Element. Statt sich für ein besseres Design zu entscheiden, ignorieren und verbergen Sie Ihr Problem. Aber das ist immer noch eine gültige Option.
19
Avi

Zur ersten Antwort von Avi & Ev0oD. Abstrakte Klassen können nur erweitert und nicht implementiert werden.

  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

Zusätzlich zu der von Avi vorgeschlagenen Lösung können Sie eine vierte Möglichkeit wählen:

Inject in Factory: Dies ist für mich die beste Option, wenn Sie bereits Code zum erneuten Makeln benötigen. Bei dieser Lösung müssen Sie den Umstellungscode nicht ändern, sondern nur die Werksklasse und den 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;
    }
}

Jetzt können Sie Ihre statische Fabrik einfach verwenden:

@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