Ich habe ein Komponenten-Setup, das im Wesentlichen ein Starter für eine Anwendung ist. Es ist wie folgt konfiguriert:
@Component
public class MyLauncher {
@Autowired
MyService myService;
//other methods
}
MyService ist mit der @Service
Spring-Annotation versehen und wird ohne Probleme automatisch in meine Launcher-Klasse übertragen.
Ich möchte einige jUnit-Testfälle für MyLauncher schreiben. Dazu habe ich eine Klasse wie diese gestartet:
public class MyLauncherTest
private MyLauncher myLauncher = new MyLauncher();
@Test
public void someTest() {
}
}
Kann ich ein Mock-Objekt für MyService erstellen und es in meiner Testklasse in myLauncher einfügen? Ich habe derzeit keinen Getter oder Setter in myLauncher, da Spring die automatische Verdrahtung übernimmt. Wenn möglich, möchte ich keine Getter und Setter hinzufügen müssen. Kann ich den Testfall anweisen, ein Scheinobjekt mithilfe einer @Before
init-Methode in die automatisch verdrahtete Variable einzufügen ?
Wenn ich das völlig falsch mache, kannst du das gerne sagen. Ich bin noch neu in diesem Bereich. Mein Hauptziel ist es, nur Java-Code oder Anmerkungen zu haben, die ein Scheinobjekt in diese @Autowired
Variable einfügen, ohne dass ich eine Setter-Methode schreiben oder eine applicationContext-test.xml
Datei verwenden muss. Ich würde viel lieber alles für die Testfälle in der .java
Datei pflegen, anstatt nur für meine Tests einen separaten Anwendungsinhalt pflegen zu müssen.
Ich hoffe, Mockito für die Scheinobjekte verwenden zu können. In der Vergangenheit habe ich dies getan, org.mockito.Mockito
indem ich meine Objekte mit verwendet und erstellt habe Mockito.mock(MyClass.class)
.
Die akzeptierte Antwort (benutze
MockitoJUnitRunner
und@InjectMocks
) ist großartig. Wenn Sie jedoch etwas Leichteres (kein spezieller JUnit-Läufer) und weniger "magisch" (transparenter) wünschen, insbesondere für den gelegentlichen Gebrauch, können Sie die privaten Felder einfach direkt mithilfe der Selbstbeobachtung festlegen.Wenn Sie Spring verwenden, haben Sie bereits eine Utility-Klasse dafür:
org.springframework.test.util.ReflectionTestUtils
Die Verwendung ist recht einfach:
ReflectionTestUtils.setField(myLauncher, "myService", myService);
Das erste Argument ist Ihre Ziel-Bean, das zweite ist der Name des (normalerweise privaten) Feldes und das letzte ist der zu injizierende Wert.
Wenn Sie Spring nicht verwenden, ist es ziemlich trivial, eine solche Dienstprogrammmethode zu implementieren. Hier ist der Code, den ich verwendet habe, bevor ich diese Spring-Klasse gefunden habe:
public static void setPrivateField(Object target, String fieldName, Object value){ try{ Field privateField = target.getClass().getDeclaredField(fieldName); privateField.setAccessible(true); privateField.set(target, value); }catch(Exception e){ throw new RuntimeException(e); } }
quelle
Manchmal können Sie Ihre
@Component
Konstruktion mithilfe eines Konstruktors oder einer Setter-basierten Injektion umgestalten , um Ihren Testfall einzurichten (Sie können und können sich immer noch darauf verlassen@Autowired
). Jetzt können Sie Ihren Test vollständig ohne ein spöttisches Framework erstellen, indem Sie stattdessen Teststubs implementieren (z. B. Martin Fowlers MailServiceStub ):@Component public class MyLauncher { private MyService myService; @Autowired MyLauncher(MyService myService) { this.myService = myService; } // other methods } public class MyServiceStub implements MyService { // ... } public class MyLauncherTest private MyLauncher myLauncher; private MyServiceStub myServiceStub; @Before public void setUp() { myServiceStub = new MyServiceStub(); myLauncher = new MyLauncher(myServiceStub); } @Test public void someTest() { } }
Diese Technik besonders nützlich , wenn der Test und die Klasse unter Test im gleichen Paket befinden sich denn dann können Sie die Standardeinstellung verwenden Paket-privaten Zugriffsmodifikator zu anderen Klassen zu verhindern , dass es erreichbar. Beachten Sie, dass Sie Ihren Produktionscode weiterhin haben können
src/main/java
, Ihre Tests jedoch insrc/main/test
Verzeichnissen.Wenn Sie Mockito mögen, werden Sie den MockitoJUnitRunner zu schätzen wissen . Es ermöglicht Ihnen, "magische" Dinge zu tun, wie Ihnen @Manuel gezeigt hat:
@RunWith(MockitoJUnitRunner.class) public class MyLauncherTest @InjectMocks private MyLauncher myLauncher; // no need to call the constructor @Mock private MyService myService; @Test public void someTest() { } }
Alternativ können Sie den Standard-JUnit-Runner verwenden und MockitoAnnotations.initMocks () in einer
setUp()
Methode aufrufen , damit Mockito die mit Anmerkungen versehenen Werte initialisiert. Weitere Informationen finden Sie im Javadoc von @InitMocks und in einem Blog-Beitrag , den ich geschrieben habe.quelle
Ich bin ein neuer Benutzer für Spring. Ich habe dafür eine andere Lösung gefunden. Reflektion verwenden und notwendige Felder öffentlich machen und Scheinobjekte zuweisen.
Dies ist mein Auth-Controller und er hat einige private Eigenschaften von Autowired.
@RestController public class AuthController { @Autowired private UsersDAOInterface usersDao; @Autowired private TokensDAOInterface tokensDao; @RequestMapping(path = "/auth/getToken", method = RequestMethod.POST) public @ResponseBody Object getToken(@RequestParam String username, @RequestParam String password) { User user = usersDao.getLoginUser(username, password); if (user == null) return new ErrorResult("Kullanıcıadı veya şifre hatalı"); Token token = new Token(); token.setTokenId("aergaerg"); token.setUserId(1); token.setInsertDatetime(new Date()); return token; } }
Und dies ist mein Junit-Test für AuthController. Ich mache öffentlich benötigte private Immobilien und weise ihnen Scheinobjekte zu und rocke :)
public class AuthControllerTest { @Test public void getToken() { try { UsersDAO mockUsersDao = mock(UsersDAO.class); TokensDAO mockTokensDao = mock(TokensDAO.class); User dummyUser = new User(); dummyUser.setId(10); dummyUser.setUsername("nixarsoft"); dummyUser.setTopId(0); when(mockUsersDao.getLoginUser(Matchers.anyString(), Matchers.anyString())) // .thenReturn(dummyUser); AuthController ctrl = new AuthController(); Field usersDaoField = ctrl.getClass().getDeclaredField("usersDao"); usersDaoField.setAccessible(true); usersDaoField.set(ctrl, mockUsersDao); Field tokensDaoField = ctrl.getClass().getDeclaredField("tokensDao"); tokensDaoField.setAccessible(true); tokensDaoField.set(ctrl, mockTokensDao); Token t = (Token) ctrl.getToken("test", "aergaeg"); Assert.assertNotNull(t); } catch (Exception ex) { System.out.println(ex); } } }
Ich kenne keine Vor- und Nachteile für diesen Weg, aber das funktioniert. Diese Technik hat etwas mehr Code, aber diese Codes können durch verschiedene Methoden usw. getrennt werden. Es gibt mehr gute Antworten auf diese Frage, aber ich möchte auf eine andere Lösung hinweisen. Entschuldigung für mein schlechtes Englisch. Habt alle ein gutes Java :)
quelle
@Autowired
scheint das Testen wirklich einfach zu machen. Ich bin mir aber auch nicht sicher, ob es die "richtige" Lösung ist.Ich glaube, damit Ihre MyLauncher-Klasse (für myService) automatisch verkabelt werden kann, müssen Sie Spring sie initialisieren lassen, anstatt den Konstruktor aufzurufen, indem Sie myLauncher automatisch verkabeln. Sobald dies automatisch verkabelt wird (und myService auch automatisch verkabelt wird), bietet Spring (1.4.0 und höher) eine @ MockBean-Annotation, die Sie in Ihren Test einfügen können. Dadurch werden übereinstimmende einzelne Beans im Kontext durch ein Modell dieses Typs ersetzt. Anschließend können Sie in einer @ Before-Methode weiter definieren, welche Verspottung Sie wünschen.
public class MyLauncherTest @MockBean private MyService myService; @Autowired private MyLauncher myLauncher; @Before private void setupMockBean() { doNothing().when(myService).someVoidMethod(); doReturn("Some Value").when(myService).someStringMethod(); } @Test public void someTest() { myLauncher.doSomething(); } }
Ihre MyLauncher-Klasse kann dann unverändert bleiben, und Ihre MyService-Bean ist ein Mock, dessen Methoden Werte zurückgeben, wie Sie sie definiert haben:
@Component public class MyLauncher { @Autowired MyService myService; public void doSomething() { myService.someVoidMethod(); myService.someMethodThatCallsSomeStringMethod(); } //other methods }
Ein paar Vorteile gegenüber anderen genannten Methoden sind:
quelle
@MockBean
brauchen@RunWith(SpringRunner.class)
?Schauen Sie sich diesen Link an
Dann schreiben Sie Ihren Testfall als
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"/applicationContext.xml"}) public class MyLauncherTest{ @Resource private MyLauncher myLauncher ; @Test public void someTest() { //test code } }
quelle