wake-up-neo.com

Mock-Objekte initialisieren - MockIto

Es gibt viele Möglichkeiten, ein Mock-Objekt mit MockIto zu initialisieren. Was ist der beste Weg unter diesen?

1.

 public class SampleBaseTestCase {

   @Before public void initMocks() {
       MockitoAnnotations.initMocks(this);
   }

2.

@RunWith(MockitoJUnitRunner.class)

[Bearbeiten] 3.

mock(XXX.class);

schlage mich vor, wenn es andere Wege gibt, die besser als diese sind 

92
VinayVeluri

Für die Scheininitialisierung mit dem Runner oder dem _MockitoAnnotations.initMocks_ sind streng gleichwertige Lösungen. Aus dem Javadoc des MockitoJUnitRunner :

JUnit 4.5 runner initializes mocks annotated with Mock, so that explicit usage of MockitoAnnotations.initMocks(Object) is not necessary. Mocks are initialized before each test method.


Die erste Lösung (mit dem _MockitoAnnotations.initMocks_) kann verwendet werden, wenn Sie bereits einen bestimmten Runner (_SpringJUnit4ClassRunner_) für Ihren Testfall konfiguriert haben.

Die zweite Lösung (mit dem MockitoJUnitRunner) ist die klassischere und meine Lieblingslösung. Der Code ist einfacher. Die Verwendung eines Läufers bietet den großen Vorteil von automatische Validierung der Framework-Nutzung (beschrieben von @ David Wallace = in diese Antwort ).

Beide Lösungen ermöglichen es, die Mocks (und Spione) zwischen den Testmethoden zu teilen. In Verbindung mit dem @InjectMocks können Unit-Tests sehr schnell geschrieben werden. Der Verspottungscode des Kesselschilds ist reduziert, die Tests sind besser lesbar. Zum Beispiel:

_@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {

    @Mock private ArticleCalculator calculator;
    @Mock(name = "database") private ArticleDatabase dbMock;
    @Spy private UserProvider userProvider = new ConsumerUserProvider();

    @InjectMocks private ArticleManager manager;

    @Test public void shouldDoSomething() {
        manager.initiateArticle();
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        manager.finishArticle();
        verify(database).removeListener(any(ArticleListener.class));
    }
}
_

Vorteile: Der Code ist minimal

Nachteile: Schwarze Magie. IMO liegt es hauptsächlich an der Annotation @InjectMocks. Mit dieser Annotation "Sie verlieren den Schmerz des Codes" (siehe die tollen Kommentare von @ Brice )


Die dritte Lösung besteht darin, Ihr Modell für jede Testmethode zu erstellen. Es erlaubt, wie durch @ mlk in seiner Antwort erklärt, " in sich geschlossenen Test " zu haben.

_public class ArticleManagerTest {

    @Test public void shouldDoSomething() {
        // given
        ArticleCalculator calculator = mock(ArticleCalculator.class);
        ArticleDatabase database = mock(ArticleDatabase.class);
        UserProvider userProvider = spy(new ConsumerUserProvider());
        ArticleManager manager = new ArticleManager(calculator, 
                                                    userProvider, 
                                                    database);

        // when 
        manager.initiateArticle();

        // then 
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        // given
        ArticleCalculator calculator = mock(ArticleCalculator.class);
        ArticleDatabase database = mock(ArticleDatabase.class);
        UserProvider userProvider = spy(new ConsumerUserProvider());
        ArticleManager manager = new ArticleManager(calculator, 
                                                    userProvider, 
                                                    database);

        // when 
        manager.finishArticle();

        // then
        verify(database).removeListener(any(ArticleListener.class));
    }
}
_

Vorteile: Sie demonstrieren deutlich, wie Ihre API funktioniert (BDD ...)

Nachteile: Es gibt mehr Kesselschild-Code. (Die verspottete Schöpfung)


Meine Empfehlung ist ein Kompromiss. Verwenden Sie die _@Mock_ -Anmerkung mit der @RunWith(MockitoJUnitRunner.class), aber verwenden Sie nicht die _@InjectMocks_:

_@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {

    @Mock private ArticleCalculator calculator;
    @Mock private ArticleDatabase database;
    @Spy private UserProvider userProvider = new ConsumerUserProvider();

    @Test public void shouldDoSomething() {
        // given
        ArticleManager manager = new ArticleManager(calculator, 
                                                    userProvider, 
                                                    database);

        // when 
        manager.initiateArticle();

        // then 
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        // given
        ArticleManager manager = new ArticleManager(calculator, 
                                                    userProvider, 
                                                    database);

        // when 
        manager.finishArticle();

        // then 
        verify(database).removeListener(any(ArticleListener.class));
    }
}
_

Vorteile: Sie demonstrieren deutlich, wie Ihre API funktioniert (wie mein ArticleManager instanziiert wird). Kein Kesselschildcode.

Nachteile: Der Test ist nicht eigenständig, weniger Code-Aufwand

127
gontard

Es gibt jetzt (ab Version 1.10.7) eine vierte Möglichkeit, Mocks zu instanziieren, die eine JUnit4 rule mit dem Namen MockitoRule verwendet.

@RunWith(JUnit4.class)   // or a different runner of your choice
public class YourTest
  @Rule public MockitoRule rule = MockitoJUnit.rule();
  @Mock public YourMock yourMock;

  @Test public void yourTestMethod() { /* ... */ }
}

JUnit sucht nach mit @Rule annotierten Unterklassen von TestRule und verwendet sie, um die Testanweisungen, die der Runner bereitstellt, mit einzuwickeln. Das Ergebnis davon ist, dass Sie @Before-Methoden, @After-Methoden extrahieren und sogar versuchen können ... Wrapper in Regeln einfangen. Sie können sogar innerhalb Ihres Tests mit diesen interagieren, wie dies bei ExpectedException der Fall ist.

MockitoRule verhält sich fast genau wie MockitoJUnitRunner, außer dass Sie jeden anderen Läufer verwenden können, wie Parametrisiert (der Ihren Testkonstruktoren Argumente erlaubt, damit Ihre Tests mehrmals ausgeführt werden können) oder Robolectrics Test Runner (damit der Classloader Java-Ersetzungen für native Android-Klassen bereitstellen kann). Dies macht es in den letzten Versionen von JUnit und Mockito strikt flexibler.

In Summe:

  • Mockito.mock(): Direkter Aufruf ohne Anmerkungsunterstützung oder Verwendungsüberprüfung.
  • MockitoAnnotations.initMocks(this): Anmerkungsunterstützung, keine Überprüfung der Verwendung.
  • MockitoJUnitRunner: Anmerkungsunterstützung und Verwendungsprüfung, aber Sie müssen diesen Läufer verwenden.
  • MockitoRule: Anmerkungsunterstützung und Verwendungsüberprüfung mit einem beliebigen JUnit-Läufer.

Siehe auch: Wie JUnit @Rule funktioniert?

22
Jeff Bowman

Es gibt einen ordentlichen Weg, dies zu tun.

  • Wenn es sich um einen Unit-Test handelt, können Sie Folgendes tun:

    @RunWith(MockitoJUnitRunner.class)
    public class MyUnitTest {
    
        @Mock
        private MyFirstMock myFirstMock;
    
        @Mock
        private MySecondMock mySecondMock;
    
        @Spy
        private MySpiedClass mySpiedClass = new MySpiedClass();
    
        // It's gonna inject the 2 mocks and the spied object per reflection to this object
        // The Java doc of @InjectMocks explains it really well how and when it does the injection
        @InjectMocks
        private MyClassToTest myClassToTest;
    
        @Test
        public void testSomething() {
        }
    }
    
  • BEARBEITEN: Wenn es sich um einen Integrationstest handelt, können Sie dies tun (nicht für die Verwendung mit Spring gedacht. Zeigen Sie einfach, dass Sie Mocks mit verschiedenen Läufern initialisieren können):

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("aplicationContext.xml")
    public class MyIntegrationTest {
    
        @Mock
        private MyFirstMock myFirstMock;
    
        @Mock
        private MySecondMock mySecondMock;
    
        @Spy
        private MySpiedClass mySpiedClass = new MySpiedClass();
    
        // It's gonna inject the 2 mocks and the spied object per reflection to this object
        // The Java doc of @InjectMocks explains it really well how and when it does the injection
        @InjectMocks
        private MyClassToTest myClassToTest;
    
        @Before
        public void setUp() throws Exception {
              MockitoAnnotations.initMocks(this);
        }
    
        @Test
        public void testSomething() {
        }
    }
    
9
emd

MockitoAnnotations & der Läufer sind oben gut besprochen worden, also werde ich meine Unterstützung für die Ungeliebten einwerfen:

XXX mockedXxx = mock(XXX.class);

Ich verwende das, weil ich es etwas beschreibender finde und ich (nicht außerhalb des richtigen Verbots) Komponententests vorziehen sollte, keine Mitgliedsvariablen zu verwenden, da meine Tests meiner Meinung nach (so viel sie können) unabhängig sind.

Die anderen Antworten sind großartig und enthalten mehr Details, wenn Sie sie wollen/brauchen.
Zusätzlich zu diesen möchte ich eine TL hinzufügen; DR:

  1. Benutze lieber
    • @RunWith(MockitoJUnitRunner.class)
  2. Wenn Sie dies nicht können (weil Sie bereits einen anderen Läufer verwenden), verwenden Sie lieber
    • @Rule public MockitoRule rule = MockitoJUnit.rule();
  3. Ähnlich wie (2), aber Sie sollten nicht dies nicht mehr verwenden:
    • @Before public void initMocks() { MockitoAnnotations.initMocks(this); }
  4. Wenn Sie in nur einem der Tests einen Schein verwenden und ihn nicht anderen Tests in derselben Testklasse aussetzen möchten, verwenden Sie
    • X x = mock(X.class)

(1) und (2) und (3) schließen sich gegenseitig aus.
(4) kann in Kombination mit den anderen verwendet werden.

0
neXus