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 @Autowired
Anmerkung 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-arg
Wert auf etwas Ungültiges setze, tritt beim Starten des Anwendungskontexts kein Fehler auf.
Antworten:
Der beste Weg ist:
Aktualisieren
In der Kontextdatei muss dieses Modell aufgelistet werden, bevor ein automatisch verdrahtetes Feld deklariert wird.
quelle
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.
quelle
mockedObject
?Mockito.spy(...)
auf diesermockedObject
Stelle? Und dann benutzewhen(mockedObject.execute).thenReturn(objToReturn)
oderdoReturn(objToReturn).when(mockedObject).execute()
. Zweitens ruft man keine echte Methode auf. Sie können auch dieMockito.doCallRealMethod()
Dokumentation überprüfenIch habe eine sehr einfache Lösung mit Spring Java Config und Mockito:
quelle
initMocks
? Warum nicht einfachreturn Mockito.mock(BeanA.class)
reingetBeanA
? Auf diese Weise ist es einfacher und es gibt weniger Code. Was vermisse ich?Gegeben:
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.
Bitte beachten Sie, dass diese Methode vor Spring 4.3.1 nicht mit Diensten hinter einem Proxy (mit Anmerkungen versehen) funktioniert
@Transactional
oderCacheable
zum 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.setField
jetzt standardmäßig der Fall ist).quelle
Wenn Sie Spring Boot 1.4 verwenden, bietet dies eine hervorragende Möglichkeit. Verwenden Sie einfach eine neue Marke
@SpringBootTest
in Ihrer Klasse und@MockBean
auf dem Feld, und Spring Boot erstellt ein Modell dieses Typs und fügt es in den Kontext ein (anstatt das Original einzufügen):Wenn Sie jedoch Spring Boot nicht verwenden oder eine frühere Version verwenden, müssen Sie etwas mehr Arbeit leisten:
Erstellen Sie eine
@Configuration
Bean, die Ihre Verspottungen in den Spring-Kontext einfügt: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
userMocks
Profil:Wenn Sie nicht den Mock, sondern die echte Bohne verwenden möchten, aktivieren Sie einfach nicht das
useMocks
Profil:quelle
web.xml
und AnnotationConfigWebApplicationContext-Setup funktioniert . Musste@WebAppConfiguration
statt@WebIntegrationTest
und@ContextHierarchy
mit@ContextConfiguration
statt verwenden@SpringApplicationConfiguration
.@Primary
Anmerkung für meinen Fall hinzufügen , da es einen fehlgeschlagenen Aufruf in einem gab@PostConstruct
, den ich verspotten wollte, aber die@PostConstruct
Bean wurde vor meinem Verspotten erstellt, sodass der Schein nicht verwendet wurde (bis ich ihn hinzufügte@Primary
).Seit 1.8.3 hat Mockito
@InjectMocks
- das ist unglaublich nützlich. Meine JUnit-Tests sind@RunWith
dieMockitoJUnitRunner
und ich erstelle@Mock
Objekte, 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
@RunWith
dasSpringJUnit4Runner
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ülltList
, und es wird keine Liste von Mock-Objekten eingefügt. Die Problemumgehung für mich bestand darin, eine@Spy
gegen 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.quelle
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 deklariertObject
. Folglich kann Spring den Bean-Typ nicht aus dem Rückgabetyp der Factory-Methode ableiten.Ronens Lösung besteht darin, eine
FactoryBean
Implementierung zu erstellen , die Mocks zurückgibt. Über dieFactoryBean
Schnittstelle kann Spring den Objekttyp abfragen, der von der Factory-Bean erstellt wurde.Meine verspottete Bohnendefinition sieht jetzt so aus:
quelle
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:
Was bedeutet, dass dies sofort funktionieren sollte:
quelle
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.
quelle
Wenn Sie spring> = 3.0 verwenden , verwenden Sie die Springs-
@Configuration
Annotation, um einen Teil des Anwendungskontexts zu definierenWenn Sie die @ImportResource nicht verwenden möchten, können Sie dies auch umgekehrt tun:
Weitere Informationen finden Sie in der Spring-Framework-Referenz: Java-basierte Containerkonfiguration
quelle
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.
quelle
Mit Mockito kann ich Folgendes tun:
quelle
Veröffentlichung einiger Beispiele basierend auf den oben genannten Ansätzen
Mit Frühling:
Ohne Frühling:
quelle
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
Aktualisieren Sie als Nächstes Ihre Spring-Konfiguration wie folgt:
quelle
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
,@Profile
und@ActiveProfiles
Anmerkungen dazu. Ich habe einen Blog-Beitrag zum Thema geschrieben.quelle
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
Dies hilft auch zu verhindern, dass Spring die Injektionen aus der verspotteten Bohne auflösen möchte.
quelle
Dies funktioniert einwandfrei, wenn es zuerst / früh in der XML-Datei deklariert wird. Mockito 1.9.0 / Spring 3.0.5
quelle
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:
Um dies zu erreichen, müssen Sie zwei einfache Hilfsklassen definieren - benutzerdefinierte Annotation (
@MockedBeans
) und eine benutzerdefinierteImportBeanDefinitionRegistrar
Implementierung.@MockedBeans
Die Annotationsdefinition muss mit Annotationen versehen sein@Import(CustomImportBeanDefinitionRegistrar.class)
undImportBeanDefinitionRgistrar
der Konfiguration in ihrerregisterBeanDefinitions
Methode müssen verspottete Beans-Definitionen hinzugefügt werden.Wenn Sie den Ansatz möchten , können Sie Probe finden Implementierungen auf meinem Blogpost .
quelle
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.
Ich habe einen Beitrag geschrieben, der das erklärt.
quelle
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
quelle
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 :)
quelle
Für die Aufzeichnung funktionieren alle meine Tests korrekt, indem nur das Gerät faul initialisiert wird, z.
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.
quelle
Wenn Sie Controller Injection verwenden, stellen Sie sicher, dass Ihre lokalen Variablen NICHT "final" sind.
quelle