Injizieren von Mockito verspottet eine Frühlingsbohne


284

Ich möchte ein Mockito-Scheinobjekt in eine Spring-Bohne (3+) injizieren, um sie mit JUnit zu testen. Meine Bean-Abhängigkeiten werden derzeit mithilfe der @AutowiredAnmerkung zu privaten Mitgliedsfeldern eingefügt.

Ich habe überlegt ReflectionTestUtils.setField, die Bean-Instanz zu verwenden, die ich injizieren möchte, ist jedoch tatsächlich ein Proxy und deklariert daher nicht die privaten Mitgliedsfelder der Zielklasse. Ich möchte keinen öffentlichen Setter für die Abhängigkeit erstellen, da ich dann meine Schnittstelle nur zu Testzwecken ändern werde.

Ich habe einige Ratschläge der Spring-Community befolgt, aber der Mock wird nicht erstellt und die automatische Verkabelung schlägt fehl:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>

Der Fehler, auf den ich derzeit stoße, lautet wie folgt:

...
Caused by: org...NoSuchBeanDefinitionException:
    No matching bean of type [com.package.Dao] found for dependency:
    expected at least 1 bean which qualifies as autowire candidate for this dependency.
    Dependency annotations: {
        @org...Autowired(required=true),
        @org...Qualifier(value=dao)
    }
at org...DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(D...y.java:901)
at org...DefaultListableBeanFactory.doResolveDependency(D...y.java:770)

Wenn ich den constructor-argWert auf etwas Ungültiges setze, tritt beim Starten des Anwendungskontexts kein Fehler auf.


4
Bitte werfen Sie einen Blick auf diese kleine Kreatur: bitbucket.org/kubek2k/springockito/wiki/Home
kubek2k

Dies ist ein sehr sauberer Ansatz - ich mag es!
Teabot

2
Sie hatten mich bei Springockito-Annotationen.
Yihtserns


2
Für diejenigen, die Spring 4. * verwenden, scheint dies ab Januar 2015 nicht mit der neuesten Spring Mockito-Version zu funktionieren, und das Projekt scheint inaktiv zu sein.
Murali

Antworten:


130

Der beste Weg ist:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock"> 
    <constructor-arg value="com.package.Dao" /> 
</bean> 

Aktualisieren
In der Kontextdatei muss dieses Modell aufgelistet werden, bevor ein automatisch verdrahtetes Feld deklariert wird.


Ich erhalte eine Fehlermeldung: "Fehler beim Erstellen der Bean mit dem Namen 'mockito': Die Bean-Definition ist abstrakt"
tttppp

4
@amra: Frühling lässt in diesem Fall nicht auf den Typ des zurückgegebenen Objekts schließen ... stackoverflow.com/q/6976421/306488
lisak

7
Ich weiß nicht, warum diese Antwort so positiv bewertet wird. Die resultierende Bean kann nicht automatisch verdrahtet werden, da sie den falschen Typ hat.
Azerole

4
Es kann automatisch verdrahtet werden, wenn es zuerst in der Kontextdatei aufgeführt wird (bevor alle automatisch verdrahteten Felder deklariert werden, die davon abhängen würden.)
Ryan Walls

3
Ab Frühjahr 3.2 spielt die Reihenfolge der Bohnen keine Rolle mehr. Siehe den Abschnitt mit dem Titel "Generic Factory Methods" in diesem Blog-Beitrag: spring.io/blog/2012/11/07/…
Ryan Walls

110
@InjectMocks
private MyTestObject testObject;

@Mock
private MyDependentObject mockedObject;

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

Dadurch werden verspottete Objekte in die Testklasse eingefügt. In diesem Fall wird mockedObject in das testObject eingefügt. Dies wurde oben erwähnt, aber hier ist der Code.


1
Wie stoppe ich eine bestimmte Methode von mockedObject?
Jim Holden

@Teinacher when (mockedObject.execute) .thenReturn (objToReturn); Sie können dies entweder in die Vorher-Methode oder in Ihre Testmethode einfügen.
Chaostheorie

40
Zu Ihrer Information: Dieser Ansatz funktioniert nicht, wenn ich in MyTestObject teilweise Autowiring und teilweise Mocking möchte.
Raksja

9
Ich weiß nicht, warum dies nicht höher gewählt wird. Wenn ich weitere Antworten mit XML sehe, werde ich schleudern.
MarkOfHall

3
Warum Sie nicht verwenden Mockito.spy(...)auf dieser mockedObjectStelle? Und dann benutze when(mockedObject.execute).thenReturn(objToReturn)oder doReturn(objToReturn).when(mockedObject).execute(). Zweitens ruft man keine echte Methode auf. Sie können auch die Mockito.doCallRealMethod()Dokumentation überprüfen
Tomasz Przybylski

63

Ich habe eine sehr einfache Lösung mit Spring Java Config und Mockito:

@Configuration
public class TestConfig {

    @Mock BeanA beanA;
    @Mock BeanB beanB;

    public TestConfig() {
        MockitoAnnotations.initMocks(this); //This is a key
    }

    //You basically generate getters and add @Bean annotation everywhere
    @Bean
    public BeanA getBeanA() {
        return beanA;
    }

    @Bean
    public BeanB getBeanB() {
        return beanB;
    }
}

4
Aus irgendeinem Grund versucht Spring bei diesem Ansatz trotzdem, die eigentliche Bohne zu erstellen (anstelle von Mock) und erstickt daran ... Was mache ich falsch?
Daniel Gruszczyk

1
Ich habe das gleiche Problem
Korobko Alex

3
Nicht Frühling, sondern Mockito versucht, eine echte Bohne zu instanziieren, wenn Sie eine Klasse verspotten. Wenn Sie Beans haben, die in Tests verspottet werden müssen, sollten diese Implementierungen einer Schnittstelle sein und über diese Schnittstelle injiziert werden. Wenn Sie dann die Schnittstelle (und nicht die Klasse) verspotten, versucht mockito nicht, diese Klasse zu instanziieren.
Daniel Gruszczyk

7
Was ist der Sinn? Warum die kommentierten Felder und den Konstruktor mit hinzufügen initMocks? Warum nicht einfach return Mockito.mock(BeanA.class)rein getBeanA? Auf diese Weise ist es einfacher und es gibt weniger Code. Was vermisse ich?
Oleg

1
@Oleg es hört sich so an, als hätten Sie Ihre eigene Lösung, die Sie wahrscheinlich als Antwort posten sollten, damit die Community darüber abstimmen kann.
Dawood ibn Kareem

48

Gegeben:

@Service
public class MyService {
    @Autowired
    private MyDAO myDAO;

    // etc
}

Sie können die zu testende Klasse über Autowiring laden lassen, die Abhängigkeit mit Mockito verspotten und dann Spring's ReflectionTestUtils verwenden, um das Modell in die zu testende Klasse einzufügen.

@ContextConfiguration(classes = { MvcConfiguration.class })
@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
    @Autowired
    private MyService myService;

    private MyDAO myDAOMock;

    @Before
    public void before() {
        myDAOMock = Mockito.mock(MyDAO.class);
        ReflectionTestUtils.setField(myService, "myDAO", myDAOMock);
    }

    // etc
}

Bitte beachten Sie, dass diese Methode vor Spring 4.3.1 nicht mit Diensten hinter einem Proxy (mit Anmerkungen versehen) funktioniert @Transactional oder Cacheablezum Beispiel). Dies wurde durch SPR-14050 behoben .

Für frühere Versionen besteht eine Lösung darin, den Proxy wie dort beschrieben zu entpacken: Durch Transaktionsanmerkungen wird vermieden, dass Dienste verspottet werden (was ReflectionTestUtils.setFieldjetzt standardmäßig der Fall ist).


Double @RunWith (SpringJUnit4ClassRunner.class) und ich verwenden unterschiedliche Anmerkungen für die Testklasse (gleicher Läufer), aber dieser Ansatz funktioniert für mich, danke.
user1317422

1
Ich war sehr inspiriert von "Bitte beachten Sie, dass diese Methode vor Spring 4.3.1 nicht mit Diensten hinter einem Proxy funktioniert (zum Beispiel mit @Transactional oder Cacheable kommentiert). Dies wurde durch SPR-14050 behoben." Ich bin gerade genau auf dieses Problem gestoßen und habe keine Ahnung bekommen, bis ich diese Worte entdeckt habe. VIELEN DANK!
Snowfox

1
Diese Lösung funktioniert, wenn Sie einen gesamten Anwendungskontext verkabelt haben und zu Testzwecken einen Mock in eine zufällige Bean in Ihrem Kontext einfügen möchten. Ich habe diese Antwort verwendet, um eine vorgetäuschte Client-Bean zu verspotten, um REST-Aufrufe an andere Module in einem Modultest zu vermeiden. Die InjectMock-Annotation funktioniert nur, wenn Sie Mocks in eine Bean injizieren, die Sie testen möchten, und nicht in eine Bean, die von der Spring Application Configuration erstellt wurde.
Andreas Lundgren

1
Fast einen ganzen Tag lang flatterte ich herum und versuchte, @MockBean zum Laufen zu bringen, ohne den Kontext zurückzusetzen, und dann stieß ich auf dieses Juwel. Genau das, was ich brauchte, Prost.
Matt R

Funktioniert, obwohl Vorsicht geboten ist, dass das ersetzte Feld aufgrund von Caching möglicherweise nicht zurückgesetzt wird und einige nicht verwandte Tests fehlschlagen können. ZB habe ich in meinem Test den Passwort-Encoder durch einen Schein-Encoder ersetzt und einige andere Tests sind aufgrund von Autorisierungsfehlern abgebrochen.
Alextsil

36

Wenn Sie Spring Boot 1.4 verwenden, bietet dies eine hervorragende Möglichkeit. Verwenden Sie einfach eine neue Marke @SpringBootTestin Ihrer Klasse und @MockBeanauf dem Feld, und Spring Boot erstellt ein Modell dieses Typs und fügt es in den Kontext ein (anstatt das Original einzufügen):

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {

    @MockBean
    private RemoteService remoteService;

    @Autowired
    private Reverser reverser;

    @Test
    public void exampleTest() {
        // RemoteService has been injected into the reverser bean
        given(this.remoteService.someCall()).willReturn("mock");
        String reverse = reverser.reverseSomeCall();
        assertThat(reverse).isEqualTo("kcom");
    }

}

Wenn Sie jedoch Spring Boot nicht verwenden oder eine frühere Version verwenden, müssen Sie etwas mehr Arbeit leisten:

Erstellen Sie eine @ConfigurationBean, die Ihre Verspottungen in den Spring-Kontext einfügt:

@Configuration
@Profile("useMocks")
public class MockConfigurer {

    @Bean
    @Primary
    public MyBean myBeanSpy() {
        return mock(MyBean.class);
    }
}

Verwenden von @Primary Annotation teilen Sie spring mit, dass diese Bean Priorität hat, wenn kein Qualifikationsmerkmal angegeben ist.

Stellen Sie sicher, dass Sie die Klasse mit Anmerkungen versehen, @Profile("useMocks")um zu steuern, welche Klassen den Mock und welche die echte Bean verwenden.

Aktivieren Sie schließlich in Ihrem Test das userMocksProfil:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
@ActiveProfiles(profiles={"useMocks"})
public class YourIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the mock!


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

Wenn Sie nicht den Mock, sondern die echte Bohne verwenden möchten, aktivieren Sie einfach nicht das useMocksProfil:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
public class AnotherIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the real implementation!


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

5
Diese Antwort sollte nach oben gehen - Die @ MockBean-Unterstützung im Spring Boot kann auch ohne Spring Boot verwendet werden. Sie können es nur in Unit-Tests verwenden, damit es für alle Federanwendungen funktioniert!
Bedrin

2
@Profile Annotation können Sie auch für die Bean-Definitionsmethode festlegen, um zu vermeiden, dass eine separate Konfigurationsklasse erstellt wird
marcin

Gute Antwort! Ich habe einige Änderungen vorgenommen, damit es mit meinem Old-School- web.xmlund AnnotationConfigWebApplicationContext-Setup funktioniert . Musste @WebAppConfigurationstatt @WebIntegrationTestund @ContextHierarchymit @ContextConfigurationstatt verwenden @SpringApplicationConfiguration.
UTF_or_Death

Ich musste die @PrimaryAnmerkung für meinen Fall hinzufügen , da es einen fehlgeschlagenen Aufruf in einem gab @PostConstruct, den ich verspotten wollte, aber die @PostConstructBean wurde vor meinem Verspotten erstellt, sodass der Schein nicht verwendet wurde (bis ich ihn hinzufügte @Primary).
Helleleye

19

Seit 1.8.3 hat Mockito @InjectMocks- das ist unglaublich nützlich. Meine JUnit-Tests sind @RunWithdie MockitoJUnitRunnerund ich erstelle @MockObjekte, die alle Abhängigkeiten für die zu testende Klasse erfüllen. Diese werden alle eingefügt, wenn das private Mitglied mit Anmerkungen versehen wird@InjectMocks .

Ich @RunWithdasSpringJUnit4Runner für Integrationstests erst jetzt.

Ich werde bemerken, dass es nicht in der Lage zu sein scheint, List<T>auf die gleiche Weise wie Spring zu injizieren . Es wird nur nach einem Mock-Objekt gesucht, das die Anforderungen erfüllt List, und es wird keine Liste von Mock-Objekten eingefügt. Die Problemumgehung für mich bestand darin, eine @Spygegen eine manuell instanziierte Liste zu verwenden und die Scheinobjekte manuell zu dieser Liste für Komponententests hinzuzufügen. Vielleicht war das beabsichtigt, weil es mich sicherlich gezwungen hat, genau darauf zu achten, was zusammen verspottet wurde.


Ja, das ist der beste Weg. Springockito injiziert die Mocks aus irgendeinem Grund in meinem Fall nicht wirklich.
Chaostheorie

13

Update: Es gibt jetzt bessere und sauberere Lösungen für dieses Problem. Bitte beachten Sie zuerst die anderen Antworten.

Ich fand schließlich eine Antwort von Ronen auf seinem Blog. Das Problem, das ich hatte, ist auf die Methode zurückzuführen Mockito.mock(Class c), die einen Rückgabetyp von deklariert Object. Folglich kann Spring den Bean-Typ nicht aus dem Rückgabetyp der Factory-Methode ableiten.

Ronens Lösung besteht darin, eine FactoryBeanImplementierung zu erstellen , die Mocks zurückgibt. Über die FactoryBeanSchnittstelle kann Spring den Objekttyp abfragen, der von der Factory-Bean erstellt wurde.

Meine verspottete Bohnendefinition sieht jetzt so aus:

<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>

1
Aktualisierter Link zu Ronens Lösung: narkisr.com/blog/2008/2647754885089732945
Jeff Martin

Ich verstehe das nicht, die Factory-Methode hat den Rückgabetyp Object ... Aber die Amra-Lösung hat einen generischen Rückgabetyp, damit Spring ihn erkennt ... Aber die Amra-Lösung funktioniert bei mir nicht
lisak

Weder diese Lösung noch Spring schließen nicht auf die Art der Bohne, die von der factoryBean zurückgegeben wird, daher keine passende Bohne vom Typ [com.package.Dao] ...
lisak


Dieser Link funktioniert tatsächlich immer noch: javadevelopmentforthemasses.blogspot.com/2008/07/… Deaktivieren Sie einfach die Linkumleitung in Ihrem Browser und Sie werden ihn sehen, anstatt gezwungen zu sein, den 404 in seinem neuen Blog anzusehen.
ungefähr

12

Ab Frühjahr 3.2 ist dies kein Problem mehr. Spring unterstützt jetzt das automatische Verdrahten der Ergebnisse generischer Factory-Methoden. Siehe den Abschnitt "Generische Factory-Methoden" in diesem Blog-Beitrag: http://spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new-testing-features/ .

Der entscheidende Punkt ist:

In Spring 3.2 werden generische Rückgabetypen für Factory-Methoden nun ordnungsgemäß abgeleitet, und die automatische Verdrahtung nach Typ für Mocks sollte wie erwartet funktionieren. Daher sind benutzerdefinierte Workarounds wie MockitoFactoryBean, EasyMockFactoryBean oder Springockito wahrscheinlich nicht mehr erforderlich.

Was bedeutet, dass dies sofort funktionieren sollte:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>

9

Der folgende Code funktioniert mit Autowiring - es ist nicht die kürzeste Version, aber nützlich, wenn es nur mit Standard-Spring- / Mockito-Gläsern funktionieren soll.

<bean id="dao" class="org.springframework.aop.framework.ProxyFactoryBean">
   <property name="target"> <bean class="org.mockito.Mockito" factory-method="mock"> <constructor-arg value="com.package.Dao" /> </bean> </property>
   <property name="proxyInterfaces"> <value>com.package.Dao</value> </property>
</bean> 

Hat für mich gearbeitet. Ich musste den Proxy in meinem Test auspacken, um ihn wie hier beschrieben zu überprüfen: forum.spring.io/forum/spring-projects/aop/…
Holgzn

9

Wenn Sie spring> = 3.0 verwenden , verwenden Sie die Springs- @ConfigurationAnnotation, um einen Teil des Anwendungskontexts zu definieren

@Configuration
@ImportResource("com/blah/blurk/rest-of-config.xml")
public class DaoTestConfiguration {

    @Bean
    public ApplicationService applicationService() {
        return mock(ApplicationService.class);
    }

}

Wenn Sie die @ImportResource nicht verwenden möchten, können Sie dies auch umgekehrt tun:

<beans>
    <!-- rest of your config -->

    <!-- the container recognize this as a Configuration and adds it's beans 
         to the container -->
    <bean class="com.package.DaoTestConfiguration"/>
</beans>

Weitere Informationen finden Sie in der Spring-Framework-Referenz: Java-basierte Containerkonfiguration


Schön. Ich habe dies verwendet, wenn der Test, den ich teste, im eigentlichen Testfall @Autowired ist.
Enkor

8

Vielleicht nicht die perfekte Lösung, aber ich benutze normalerweise keine Feder, um DI für Unit-Tests durchzuführen. Die Abhängigkeiten für eine einzelne Bean (die zu testende Klasse) sind normalerweise nicht allzu komplex, daher führe ich die Injektion einfach direkt im Testcode durch.


3
Ich verstehe Ihren Ansatz. Ich befinde mich jedoch in dieser Situation auf einer großen Legacy-Codebasis, die dies noch nicht so einfach zulässt.
Teabot

1
Ich habe festgestellt, dass die Mockito / Spring-Kombination sehr nützlich ist, wenn ich Code testen muss, der stark von Spring-Aspekten / AOP abhängt (zum Beispiel beim Testen von Spring-Sicherheitsregeln). Obwohl man zu Recht behauptet, dass solche Tests ein Integrationstest sein sollten.
Lars Tackmann

@Lars - stimmte zu - das gleiche gilt für die Tests, mit denen ich mich befasse.
Teabot

7

Mit Mockito kann ich Folgendes tun:

<bean id="stateMachine" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.abcd.StateMachine"/>
</bean>

1
Danke für die Antwort @Alexander. Darf ich fragen: Ist die Verkabelung korrekt? Wenn ja, welche Versionen von Spring / Mockito verwenden Sie?
Teabot

6

Veröffentlichung einiger Beispiele basierend auf den oben genannten Ansätzen

Mit Frühling:

@ContextConfiguration(locations = { "classpath:context.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService;
    @Mock
    private TestService2 testService2;
}

Ohne Frühling:

@RunWith(MockitoJUnitRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService = new TestServiceImpl();
    @Mock
    private TestService2 testService2;
}

2

Update - neue Antwort hier: https://stackoverflow.com/a/19454282/411229 . Diese Antwort gilt nur für Spring-Versionen vor 3.2.

Ich habe eine Weile nach einer endgültigeren Lösung dafür gesucht. Dieser Blog-Beitrag scheint alle meine Bedürfnisse abzudecken und ist nicht auf die Bestellung von Bean-Deklarationen angewiesen. Alle Ehre gebührt Mattias Severson. http://www.jayway.com/2011/11/30/spring-integration-tests-part-i-creating-mock-objects/

Implementieren Sie grundsätzlich eine FactoryBean

package com.jayway.springmock;

import org.mockito.Mockito;
import org.springframework.beans.factory.FactoryBean;

/**
 * A {@link FactoryBean} for creating mocked beans based on Mockito so that they 
 * can be {@link @Autowired} into Spring test configurations.
 *
 * @author Mattias Severson, Jayway
 *
 * @see FactoryBean
 * @see org.mockito.Mockito
 */
public class MockitoFactoryBean<T> implements FactoryBean<T> {

    private Class<T> classToBeMocked;

    /**
     * Creates a Mockito mock instance of the provided class.
     * @param classToBeMocked The class to be mocked.
     */
    public MockitoFactoryBean(Class<T> classToBeMocked) {
        this.classToBeMocked = classToBeMocked;
    }

    @Override
    public T getObject() throws Exception {
        return Mockito.mock(classToBeMocked);
    }

    @Override
    public Class<?> getObjectType() {
        return classToBeMocked;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

Aktualisieren Sie als Nächstes Ihre Spring-Konfiguration wie folgt:

<beans...>
    <context:component-scan base-package="com.jayway.example"/>

    <bean id="someDependencyMock" class="com.jayway.springmock.MockitoFactoryBean">
        <constructor-arg name="classToBeMocked" value="com.jayway.example.SomeDependency" />
    </bean>
</beans>

2

Betrachtet man das Entwicklungstempo von Springockito und die Anzahl der offenen Fragen , würde ich wenig sein besorgt , dass es in meinen Test - Suite einzuführen Stapel heutzutage. Die Tatsache, dass die letzte Version vor der Veröffentlichung von Spring 4 erstellt wurde, wirft Fragen auf wie "Ist es möglich, sie einfach in Spring 4 zu integrieren?". Ich weiß es nicht, weil ich es nicht ausprobiert habe. Ich bevorzuge den reinen Spring-Ansatz, wenn ich Spring Bean im Integrationstest verspotten muss.

Es besteht die Möglichkeit, Spring Bean mit einfachen Spring-Funktionen zu fälschen. Sie verwenden müssen @Primary, @Profileund @ActiveProfilesAnmerkungen dazu. Ich habe einen Blog-Beitrag zum Thema geschrieben.


1

Ich fand eine ähnliche Antwort wie Teabot, um eine MockFactory zu erstellen, die die Mocks bereitstellt. Ich habe das folgende Beispiel verwendet, um die Mock-Factory zu erstellen (da der Link zu narkisr tot ist): http://hg.randompage.org/java/src/407e78aa08a0/projects/bookmarking/backend/spring/src/test/java/ org / randompage / bookmarking / backend / testUtils / MocksFactory.java

<bean id="someFacade" class="nl.package.test.MockFactory">
    <property name="type" value="nl.package.someFacade"/>
</bean>

Dies hilft auch zu verhindern, dass Spring die Injektionen aus der verspotteten Bohne auflösen möchte.


1
<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>

Dies funktioniert einwandfrei, wenn es zuerst / früh in der XML-Datei deklariert wird. Mockito 1.9.0 / Spring 3.0.5


1

Ich verwende eine Kombination aus dem Ansatz, den Markus T als Antwort verwendet, und einer einfachen Hilfsimplementierung ImportBeanDefinitionRegistrar, die nach einer benutzerdefinierten Annotation ( @MockedBeans) sucht, in der angegeben werden kann, welche Klassen verspottet werden sollen. Ich glaube, dass dieser Ansatz zu einem prägnanten Komponententest führt, bei dem ein Teil des Boilerplate-Codes für Spott entfernt wurde.

So sieht ein Beispiel-Unit-Test mit diesem Ansatz aus:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class ExampleServiceIntegrationTest {

    //our service under test, with mocked dependencies injected
    @Autowired
    ExampleService exampleService;

    //we can autowire mocked beans if we need to used them in tests
    @Autowired
    DependencyBeanA dependencyBeanA;

    @Test
    public void testSomeMethod() {
        ...
        exampleService.someMethod();
        ...
        verify(dependencyBeanA, times(1)).someDependencyMethod();
    }

    /**
     * Inner class configuration object for this test. Spring will read it thanks to
     * @ContextConfiguration(loader=AnnotationConfigContextLoader.class) annotation on the test class.
     */
    @Configuration
    @Import(TestAppConfig.class) //TestAppConfig may contain some common integration testing configuration
    @MockedBeans({DependencyBeanA.class, DependencyBeanB.class, AnotherDependency.class}) //Beans to be mocked
    static class ContextConfiguration {

        @Bean
        public ExampleService exampleService() {
            return new ExampleService(); //our service under test
        }
    }
}

Um dies zu erreichen, müssen Sie zwei einfache Hilfsklassen definieren - benutzerdefinierte Annotation ( @MockedBeans) und eine benutzerdefinierte ImportBeanDefinitionRegistrarImplementierung. @MockedBeansDie Annotationsdefinition muss mit Annotationen versehen sein @Import(CustomImportBeanDefinitionRegistrar.class)und ImportBeanDefinitionRgistrarder Konfiguration in ihrer registerBeanDefinitionsMethode müssen verspottete Beans-Definitionen hinzugefügt werden.

Wenn Sie den Ansatz möchten , können Sie Probe finden Implementierungen auf meinem Blogpost .


1

Ich habe eine Lösung entwickelt, die auf dem Vorschlag von Kresimir Nesek basiert. Ich habe eine neue Anmerkung @EnableMockedBean hinzugefügt , um den Code etwas sauberer und modularer zu gestalten.

@EnableMockedBean
@SpringBootApplication
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes=MockedBeanTest.class)
public class MockedBeanTest {

    @MockedBean
    private HelloWorldService helloWorldService;

    @Autowired
    private MiddleComponent middleComponent;

    @Test
    public void helloWorldIsCalledOnlyOnce() {

        middleComponent.getHelloMessage();

        // THEN HelloWorldService is called only once
        verify(helloWorldService, times(1)).getHelloMessage();
    }

}

Ich habe einen Beitrag geschrieben, der das erklärt.


1

Ich würde vorschlagen, Ihr Projekt auf Spring Boot 1.4 zu migrieren. Danach können Sie neue Anmerkungen verwenden @MockBean, um Ihre zu fälschencom.package.Dao


0

Heute habe ich herausgefunden, dass ein Frühlingskontext, in dem ich einen vor den Mockito-Bohnen deklariert habe, nicht geladen werden konnte. Nach dem Verschieben des AFTER the Mocks wurde der App-Kontext erfolgreich geladen. Pass auf :)


1
Da fehlt etwas. 8-) Du hast was nach den Verspottungen bewegt?
Hans-Peter Störr

0

Für die Aufzeichnung funktionieren alle meine Tests korrekt, indem nur das Gerät faul initialisiert wird, z.

<bean id="fixture"
      class="it.tidalwave.northernwind.rca.embeddedserver.impl.DefaultEmbeddedServer"
      lazy-init="true" /> <!-- To solve Mockito + Spring problems -->

<bean class="it.tidalwave.messagebus.aspect.spring.MessageBusAdapterFactory" />

<bean id="applicationMessageBus"
      class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="it.tidalwave.messagebus.MessageBus" />
</bean>

<bean class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="javax.servlet.ServletContext" />
</bean>

Ich nehme an, die Begründung ist die, die Mattias hier (am Ende des Beitrags) erklärt, dass eine Problemumgehung die Reihenfolge ändert, in der die Beans deklariert werden - eine verzögerte Initialisierung ist "irgendwie", wenn das Fixture am Ende deklariert wird.


-1

Wenn Sie Controller Injection verwenden, stellen Sie sicher, dass Ihre lokalen Variablen NICHT "final" sind.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.