Über das Testen mit Mock in JUnit. Dies ist der zweite Artikel zur Verbreitung von Testcode am Arbeitsplatz. Es ist keine sehr junge Geschichte, da sie sich auf EasyMock konzentriert.
Ein Mock-Objekt ist eine Art unterer Modul-Stub, der Softwaretests ersetzt, insbesondere bei testgetriebener Entwicklung und verhaltensgetriebener Entwicklung. Zu prüfendes Modul im Vergleich zum Stummel Wird verwendet, um zu überprüfen, ob die Submodule korrekt verwendet werden.
Scheinobjekt Quelle: Freie Enzyklopädie "Wikipedia"
Angenommen, Sie haben eine Klasse namens HelloJunit wie unten gezeigt erstellt, um sie zu verwenden. Wenn Sie versuchen, diese Klasse zu testen, wenn die GreetDao-Klasse nicht erstellt wird (und die von Dao referenzierte Datenbank nicht vorbereitet ist), tritt beim normalen Testen ein Fehler auf. Ich denke, es wird enden.
HelloJunit
public class HelloJunit {
@Binding
GreetDao greetDao;
public String sayGreeting() {
return greetDao.getGreet();
}
Ursprünglich möchte ich nur die HelloJunit-Klasse testen, damit ich sie auch dann testen kann, wenn der Code der Abhängigkeit (GreetDao) nicht erstellt wurde. Bereiten wir also eine Haribote-Klasse vor, damit sie vorerst funktioniert. Das ist die Rolle des Mocks.
Es gibt die folgenden Methoden.
Zu den Mock-Bibliotheken gehören "Mockito", "JMockit" und "EasyMock", "MockInterceptor".
Ich habe den Eindruck, dass "Mockito" heutzutage oft verwendet wird.
In Seasar2 sind "EasyMock" und "MockInterceptor" standardmäßig enthalten. Ich denke, es ist schwierig, eine neue Bibliothek in einer Umgebung zu installieren, in der Seasar2 noch verwendet wird. Daher möchte ich dieses Mal mit EasyMock fortfahren.
Der Produktcode wurde gegenüber dem oben eingeführten Inhalt geringfügig geändert.
Produktcode
public class HelloJunit {
GreetDao greetDao;
public HelloJunit(GreetDao greetDao) {
this.greetDao = greetDao;
}
public String sayGreeting() {
return greetDao.getGreet();
}
}
Hier ist der Testcode.
Testcode
import static org.easymock.EasyMock.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.seasar.framework.unit.annotation.EasyMock;
import org.seasar.sastruts.example.dao.GreetDao;
public class HelloJunitTest {
HelloJunit helloJunit;
@EasyMock
GreetDao greetDao;
@Before
public void setUp() {
greetDao = createMock(GreetDao.class);
helloJunit = new HelloJunit(greetDao);
}
@Test
öffentliche Leere sagen einen Gruß() throws Exception {
//installieren
expect(greetDao.getGreet()).andReturn("Hello");
replay(greetDao);
//Übung
String actual = helloJunit.sayGreeting();
//überprüfen
String expected = "Hello";
assertThat(actual, is(expected));
verify(greetDao);
//abreißen (Nachbearbeitung)
//wenn Sie Probleme haben ···
}
}
Ich denke, Sie können es irgendwie verstehen, aber ich werde es erklären.
Ich habe GreetDao als Mitgliedsvariable, aber es hat eine "EasyMock-Annotation". Durch Anhängen können Sie EasyMock verwenden.
createMock Die setUp-Methode verwendet die Methode createMock, um ein greetDao zu erstellen. Der Mock wird festgelegt, indem das hier erstellte greetDao beim Erstellen der Instanz von helloJunit übergeben wird, bei der es sich um die Testzielmethode handelt.
Die "Expect" -Methode wird im Kommentar-Setup verwendet. Dies ist der wichtigste Teil, der das Verhalten des Mocks bestimmt.
Wenn greetDao.getGreet () so aufgerufen wird, wird ~ zurückgegeben.
Geben Sie an, wie die Methode mit expected ()
aufgerufen werden soll. Verwenden Sie dann andReturn ()
, um den Rückgabewert festzulegen.
In diesem Fall wird "Hallo" zurückgegeben, wenn greetDao.getGreet () ohne Argument aufgerufen wird.
Die Methode "Wiedergabe" ist ein Bild, das sich an das Verhalten des unmittelbar vorher festgelegten Mock-Sets erinnert. Stellen Sie sicher, dass Sie nach der Ausführung von expected () erneut spielen.
exercise Hier wird die zu testende Methode ausgeführt und in einer Variablen gespeichert. Es wird gesagt, dass es leicht zu verstehen ist, ob die gespeicherten Variablen unabhängig vom Typ zu "tatsächlich" vereinheitlicht werden.
verify
Hier vergleichen wir den Rückgabewert und den erwarteten Wert von helloJunit.sayGreeting ();
.
Außerdem überprüft verify (greetDao);
, ob das im Setup angegebene Verhalten des Mocks wie erwartet aufgerufen wurde.
Wenn helloJunit.sayGreeting ();
noch nie aufgerufen wurde, wird ein Fehler zurückgegeben.
tear down Dies ist die Nachbearbeitung nach dem Test. Wenn beispielsweise Testdaten in die Datenbank eingegeben wurden, werden diese Daten gelöscht.
Überprüfen Sie diesmal, ob die findDreet-Methode von greetDao wie erwartet aufgerufen wird.
Zu testender Code
public String sayGreeting() {
return greetDao.findGreet(1);
}
Schreiben Sie in expected ()
die erwartete Methodenaufrufmethode so wie sie ist.
In diesem Fall wird angenommen, dass 1 im Argument von "findGreet" enthalten ist. Beschreiben Sie also einfach "findGreet (1)".
Testcode
@Test
öffentlicher Test für nichtige Argumente() throws Exception {
//installieren
expect(greetDao.findGreet(1)).andReturn("Hello");
replay(greetDao);
//Übung
String actual = helloJunit.sayGreeting();
//überprüfen
verify(greetDao);
}
Übrigens, wenn es von findGreet (2);
aufgerufen wird, wird der folgende Fehler ausgegeben.
Referenz im Fehlerfall
java.lang.AssertionError:
Unexpected method call findGreet(2):
findGreet(1): expected: 1, actual: 0
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:32)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:61)
at com.sun.proxy.$Proxy4.findGreet(Unknown Source)
at org.seasar.sastruts.example.service.HelloJunit.sayGreeting(HelloJunit.java:17)
at org.seasar.sastruts.example.service.HelloJunitTest.Argument Test(HelloJunitTest.java:52)
--Danach weggelassen--
Ich habe versucht, einen Zweig mit dem Rückgabewert von findGreet
einzufügen.
In diesem Test möchte ich das Muster testen, in dem Nudeln zurückgegeben werden.
Produktionscode
public String sayGreeting() {
String greet = greetDao.findGreet(1);
if (greet.contentEquals("Hello")) {
return "Pasta";
} else {
return "Ramen";
}
}
Um den Rückgabewert festzulegen, geben Sie den Rückgabewert in "andReturn" an. In diesem Fall wird "Hallo" zurückgegeben.
Testcode
@Test
öffentlicher Test für nichtige Argumente() throws Exception {
//installieren
expect(greetDao.findGreet(1)).andReturn("Hello");
replay(greetDao);
//Übung
String actual = helloJunit.sayGreeting();
//überprüfen
String expected = "Pasta";
assertThat(actual, is(expected));
verify(greetDao);
}
So testen Sie eine Methode vom Typ void.
Produktionscode (GreetDao)
public interface GreetDao {
public void insertGreet(String greet);
}
Produktionscode (HelloJunit)
public String addGreet() {
greetDao.insertGreet("Good Night");
return "OK";
}
Wenn Sie den Void-Typ testen möchten, führen Sie die Void-Methode in Ihrem Testcode aus.
Rufen Sie dann expectedLastCall ()
auf.
Auf diese Weise können Sie sich die letzte Methodenausführung merken und überprüfen.
Übrigens, wenn dieselbe void-Methode zweimal ausgeführt wird, wird durch Hinzufügen von times () wie "awaitLastCall (). Time (2);" überprüft, ob sie so oft ausgeführt wurde.
Testcode
@Test
Test Public Void Void Methode() throws Exception {
//installieren
greetDao.insertGreet("Good Night");
expectLastCall();
replay(greetDao);
//Übung
String actual = helloJunit.sayGreeting();
//überprüfen
verify(greetDao);
}
EasyMock kann nur Schnittstellenklassen verspotten. Wenn Sie eine konkrete Klasse verspotten möchten, versuchen Sie die folgende Vererbungsmethode.
Wenn die in der zu testenden Methode verwendete Klasse keine Schnittstelle ist oder das Testen unpraktisch ist, können Sie sie zum Testen erben und Ihre eigene Scheinklasse vorbereiten.
Wenn beispielsweise die von der Methode zurückgegebenen Beans die Methode equals nicht überschreiben und es schwierig ist, jedes Feld zu überprüfen, erben Sie equals und testen Sie wie folgt.
Zu testender Produktionscode
public TransferResultDto transfer(String payerAccountId,
String payeeAccountId,
String payerName,
long transferAmount) {
Balance payerBalance = balanceDao.findByAccountId(payerAccountId);
if (payerBalance == null)
throw new BusinessLogicException("Ich kann das Gleichgewicht nicht bekommen");
if (transferAmount > payerBalance.amount)
throw new BusinessLogicException("Nicht genug Gleichgewicht");
Balance payeeBalance = balanceDao.findByAccountId(payeeAccountId);
if (payeeBalance == null)
throw new BusinessLogicException("Das Überweisungskonto existiert nicht");
LocalDateTime transferDate = LocalDateTime.now();
Transfer peyerTransaction = new Transfer();
peyerTransaction.accountId = payerAccountId;
peyerTransaction.name = payerBalance.name;
peyerTransaction.transferAmount = -transferAmount;
peyerTransaction.transferDate = transferDate;
Transfer payeeTransaction = new Transfer();
payeeTransaction.accountId = payeeAccountId;
payeeTransaction.name = payeeBalance.name;
payeeTransaction.transferAmount = transferAmount;
payeeTransaction.transferDate = transferDate;
transferDao.insertTransfer(peyerTransaction);
transferDao.insertTransfer(payeeTransaction);
balanceDao.updateAmount(payerAccountId, -transferAmount);
balanceDao.updateAmount(payeeAccountId, transferAmount);
Balance updatedPayerBalance = balanceDao.findByAccountId(payerAccountId);
return new TransferResultDto(payerAccountId, payeeAccountId,
payerBalance.name, payeeBalance.name,
transferAmount,
updatedPayerBalance.amount);
}
}
Bring Bean zurück
public class TransferResultDto {
private final String payerAccountId;
private final String payeeAccountId;
private final String payerName;
private final String payeeName;
private final long transferAmount;
private final long amount;
public TransferResultDto(String payerAccountId,
String payeeAccountId,
String payerName,
String payeeName,
long transferAmount,
long amount) {
this.payerAccountId = payerAccountId;
this.payeeAccountId = payeeAccountId;
this.payerName = payerName;
this.payeeName = payeeName;
this.transferAmount = transferAmount;
this.amount = amount;
}
public String getPayerAccountId() {
return payerAccountId;
}
public String getPayeeAccountId() {
return payeeAccountId;
}
public String getPayerName() {
return payerName;
}
public String getPayeeName() {
return payeeName;
}
public long getTransferAmount() {
return transferAmount;
}
public long getAmount() {
return amount;
}
}
Um dies zu testen, erstellen Sie eine "TransferResultDtoMock" -Klasse, die von "TransferResultDto" in der Testklasse erbt. Bereiten Sie in dieser Klasse "TransferResultDtoMock" eine Methode equals vor, die alle Felder vergleicht. Beim Testen können Sie mit "assertThat (actual, is (erwartet))" vergleichen.
python
public class TransferServiceTest {
@Test
public void try vererbung verspotten() {
// setup
TransferResultDtoMock expected =
new TransferResultDtoMock("1", "2",
ACCOUNT1_BEFORE_BALANCE.name, ACCOUNT2_BALANCE.name,
2000, ACCOUNT1_AFTER_BALANCE.amount);
TransferResultDto transferResultDto = transferService.transfer("1", "2", "Taro Tanaka", 1000);
TransferResultDtoMock actual = new TransferResultDtoMock(transferResultDto);
assertThat(actual, is(expected));
}
@EqualsAndHashCode(callSuper = true)
private class TransferResultDtoMock extends TransferResultDto {
public TransferResultDtoMock(String payerAccountId,
String payeeAccountId,
String payerName,
String payeeName,
long transferAmount,
long amount) {
super(payerAccountId, payeeAccountId, payerName,
payeeName, transferAmount, amount);
}
public TransferResultDtoMock(TransferResultDto transferResultDto) {
super(transferResultDto.getPayerAccountId(),
transferResultDto.getPayeeAccountId(),
transferResultDto.getPayerName(),
transferResultDto.getPayeeName(),
transferResultDto.getTransferAmount(),
transferResultDto.getAmount());
}
}
}
Zunächst haben Sie die Logik in HelloJunit von Annotation auf Konstruktortyp in DI GreetDao geändert. Der Grund dafür ist, dass HelloJunit, wenn es durch Anmerkungen ausgedrückt wird, je nach Framework (Seasar2) zu einer ** abhängigen </ font> ** Form wird.
Produktcode
public class HelloJunit {
GreetDao greetDao;
public HelloJunit(GreetDao greetDao) {
this.greetDao = greetDao;
}
public String sayGreeting() {
return greetDao.getGreet();
}
}
Wenn Sie sich beispielsweise für einen zukünftigen Wechsel zu Spring entscheiden, verwenden Sie @ Autowired to DI mit Anmerkungen. Daher ist es einfach, durch DI im Konstruktor auf das Framework zu wechseln.
Unter dem Gesichtspunkt des Testcodes muss beim Deklarieren der Klasse "@ RunWith (Seasar2.class)" angegeben werden, wenn dies durch Annotation realisiert wird. Wenn es sich jedoch um DI im Konstruktor handelt, ist new Wenn Sie dies tun, können Sie leicht testen.
Einfach zu schreibender Code ist wichtig zum Testen.
Der folgende Code erstellt beispielsweise eine bedingte Verzweigung gemäß dem Ergebnis der statischen Methode "TimeJunit.getTime ()". Es ist jedoch schwierig, die statische Methode durch eine Scheinmethode zu ersetzen.
Mit statischer Methode
public class ExampleJunit {
public String sayGreeting() {
int term = TimeJunit.getTime();
switch(term) {
case 1:
return "Pasta";
case 2:
return "Ramen";
default:
return "Reis";
}
}
}
Der Rest des Codes ist schwer zu testen, selbst wenn er vererbt wird und Methoden der Oberklasse verwendet. Zum Beispiel diese Art von Logik, die häufig in älteren Systemen verwendet wird.
Super Klasse
public abstract class AbstractJunit {
public String sayGreeting() {
return this.getMessage();
}
abstract protected String getMessage();
}
Vererbungsklasse
public class ExampleJunit extends AbstractJunit {
protected String getMessage() {
int term = 1;
switch(term) {
case 1:
return "Pasta";
case 2:
return "Ramen";
default:
return "Reis";
}
}
}
In diesem Beispiel kann es getestet werden, aber in dem Fall, in dem Vererbung tatsächlich im Geschäftsleben verwendet wird, ist die Superklasse eine Gottklasse (eine bequeme Klasse, um Logik in irgendetwas zu setzen) und eine Spaghettiquelle. Es ist umständlich, weil ich im Test nachdenken muss.
Überlegen Sie, ob Sie die Vererbung wirklich verwenden und implementieren sollten.
Code, der einfach zu schreiben ist, ist guter Code
http://s2container.seasar.org/2.4/ja/easyMock.html
Recommended Posts