Unit Test - Was ist Spott? und warum?
Im Spott werden Objekte erstellt, die das Verhalten realer Objekte nachahmen. Lesen Sie die folgende Fallstudie:
Geprüft :
-
Java 1.8
-
Einheit 4.12
-
Mockito 2.0.73-beta
Mock Object
Lesen Sie dieseWikipedia Mock
object.
1. Java-Beispiele
Ein einfaches Beispiel für Autor und Buch.
1.1 BookService
to return a list of books by author name.
BookService.java
package com.example.examples.mock; import java.util.List; public interface BookService { ListfindBookByAuthor(String author); }
BookServiceImpl.java
package com.example.examples.mock; import java.util.List; public class BookServiceImpl implements BookService { private BookDao bookDao; public BookDao getBookDao() { return bookDao; } public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } @Override public ListfindBookByAuthor(String name) { return bookDao.findBookByAuthor(name); } }
BookDao.java
package com.example.examples.mock; import java.util.List; public interface BookDao { ListfindBookByAuthor(String author); }
BookDaoImpl.java
package com.example.examples.mock; import java.util.List; public class BookDaoImpl implements BookDao { @Override public ListfindBookByAuthor(String name) { // init database // Connect to DB for data // return data } }
1.2 A book validator.
BookValidatorService.java
package com.example.examples.mock; public interface BookValidatorService { boolean isValid(Book book); }
FakeBookValidatorService.java
package com.example.examples.mock; public class FakeBookValidatorService implements BookValidatorService { @Override public boolean isValid(Book book) { if (book == null) return false; if ("bot".equals(book.getName())) { return false; } else { return true; } } }
1.3 Reviews the AuthorServiceImpl
, it has dependencies on BookService
(depends on BookDao
) and BookValidatorService
, it makes the unit test a bit hard to write.
AuthorService.java
package com.example.examples.mock; public interface AuthorService { int getTotalBooks(String author); }
AuthorServiceImpl.java
package com.example.examples.mock; import java.util.List; import java.util.stream.Collectors; public class AuthorServiceImpl implements AuthorService { private BookService bookService; private BookValidatorService bookValidatorService; public BookValidatorService getBookValidatorService() { return bookValidatorService; } public void setBookValidatorService(BookValidatorService bookValidatorService) { this.bookValidatorService = bookValidatorService; } public BookService getBookService() { return bookService; } public void setBookService(BookService bookService) { this.bookService = bookService; } //How to test this method ??? @Override public int getTotalBooks(String author) { Listbooks = bookService.findBookByAuthor(author); //filters some bot writers List filtered = books.stream().filter( x -> bookValidatorService.isValid(x)) .collect(Collectors.toList()); //other business logic return filtered.size(); } }
2. Gerätetest
Erstellen Sie einen Komponententest fürAuthorServiceImpl.getTotalBooks()
2.1 The AuthorServiceImpl
has two dependencies, you need to make sure both are configured properly.
AuthorServiceTest.java
package com.example.mock; import com.example.examples.mock.AuthorServiceImpl; import com.example.examples.mock.BookDaoImpl; import com.example.examples.mock.BookServiceImpl; import com.example.examples.mock.FakeBookValidatorService; import org.junit.Test; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; public class AuthorServiceTest { @Test public void test_total_book_by_mock() { //1. Setup AuthorServiceImpl obj = new AuthorServiceImpl(); BookServiceImpl bookService = new BookServiceImpl(); bookService.setBookDao(new BookDaoImpl()); //Where Dao connect to? obj.setBookService(bookService); obj.setBookValidatorService(new FakeBookValidatorService()); //2. Test method int qty = obj.getTotalBooks("example"); //3. Verify result assertThat(qty, is(2)); } }
Um den obigen Komponententest zu bestehen, müssen Sie eine Datenbank in der DAO-Schicht einrichten. Andernfalls gibtbookService
nichts zurück.
2.3 Some disadvantages to perform tests like above :
-
Dieser Komponententest ist langsam, da Sie eine Datenbank starten müssen, um Daten von DAO abzurufen.
-
Dieser Komponententest ist nicht isoliert, sondern hängt immer von externen Ressourcen wie der Datenbank ab.
-
Dieser Komponententest kann nicht sicherstellen, dass die Testbedingungen immer gleich sind. Die Daten in der Datenbank können zeitlich variieren.
-
Es ist zu viel Arbeit, eine einfache Methode zu testen, da Entwickler den Test überspringen.
2.4 Solution
The solution is obvious, you need a modified version of the BookServiceImpl
class – which will always return the same data for testing, a mock object!
What is mocking?
Auch hier werden beim Verspotten Objekte erstellt, die das Verhalten realer Objekte nachahmen.
3. Unit Test - Scheinobjekt
3.1 Create a new MockBookServiceImpl
class and always return the same collection of books for author “example”.
MockBookServiceImpl.java
package com.example.mock; import com.example.examples.mock.Book; import com.example.examples.mock.BookService; import java.util.ArrayList; import java.util.List; //I am a mock object! public class MockBookServiceImpl implements BookService { @Override public ListfindBookByAuthor(String author) { List books = new ArrayList<>(); if ("example".equals(author)) { books.add(new Book("example in action")); books.add(new Book("abc in action")); books.add(new Book("bot")); } return books; } //implements other methods... }
3.2 Update the unit test again.
AuthorServiceTest.java
package com.example.mock; import com.example.examples.mock.AuthorServiceImpl; import com.example.examples.mock.FakeBookValidatorService; import org.junit.Test; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; public class AuthorServiceTest { @Test public void test_total_book_by_mock() { //1. Setup AuthorServiceImpl obj = new AuthorServiceImpl(); /*BookServiceImpl bookService = new BookServiceImpl(); bookService.setBookDao(new BookDaoImpl()); obj.setBookService(bookService);*/ obj.setBookService(new MockBookServiceImpl()); obj.setBookValidatorService(new FakeBookValidatorService()); //2. Test method int qty = obj.getTotalBooks("example"); //3. Verify result assertThat(qty, is(2)); } }
Der obige Komponententest ist viel besser, schnell, isoliert (keine Datenbank mehr) und die Testbedingungen (Daten) sind immer gleich.
3.3 But, there are some disadvantages to create mock object manually like above :
-
Am Ende können Sie viele Scheinobjekte (Klassen) nur für den Unit-Test-Zweck erstellen.
-
Wenn die Schnittstelle viele Methoden enthält, müssen Sie jede überschreiben.
-
Es ist immer noch zu viel Arbeit und chaotisch!
3.4 Solution
Try Mockito, a simple and powerful mocking framework.
4. Unit Test - Mockito
4.1 Update the unit test again, this time, create the mock object via Mockito framework.
pom.xml
org.mockito mockito-core 2.0.73-beta
AuthorServiceTest.java
package com.example.mock; import com.example.examples.mock.AuthorServiceImpl; import com.example.examples.mock.Book; import com.example.examples.mock.BookServiceImpl; import com.example.examples.mock.FakeBookValidatorService; import org.junit.Test; import java.util.Arrays; import java.util.List; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class AuthorServiceTest { @Test public void test_total_book_by_mockito() { //1. Setup Listbooks = Arrays.asList( new Book("example in action"), new Book("abc in action"), new Book("bot")); BookServiceImpl mockito = mock(BookServiceImpl.class); //if the author is "example", then return a 'books' object. when(mockito.findBookByAuthor("example")).thenReturn(books); AuthorServiceImpl obj = new AuthorServiceImpl(); obj.setBookService(mockito); obj.setBookValidatorService(new FakeBookValidatorService()); //2. Test method int qty = obj.getTotalBooks("example"); //3. Verify result assertThat(qty, is(2)); } }
Erledigt. Ihr Feedback wird geschätzt