単体テスト–モッキングとは何ですか? なぜ?
簡単に言えば、モックとは、実際のオブジェクトの動作を模倣するオブジェクトを作成することです。 次のケーススタディを参照してください。
テスト済み
-
Java 1.8
-
JUnit 4.12
-
Mockito 2.0.73-ベータ
Mock Object
このWikipedia Mock
objectを読み取ります。
1. Javaの例
簡単な著者と本の例。
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. 単体テスト
AuthorServiceImpl.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)); } }
上記の単体テストに合格するには、DAOレイヤーにデータベースをセットアップする必要があります。そうしないと、bookService
は何も返しません。
2.3 Some disadvantages to perform tests like above :
-
DAOからデータを取得するにはデータベースを起動する必要があるため、この単体テストは低速です。
-
この単体テストは分離されておらず、常にデータベースなどの外部リソースに依存しています。
-
この単体テストでは、テスト条件が常に同じであることを保証できません。データベース内のデータは時間とともに変化する可能性があります。
-
簡単な方法をテストするには手間がかかりすぎて、開発者はテストをスキップします。
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?
繰り返しますが、モックは実際のオブジェクトの動作を模倣するオブジェクトを作成しています。
3. ユニットテスト-モックオブジェクト
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)); } }
上記の単体テストは、はるかに優れており、高速で、分離されており(データベースは不要)、テスト条件(データ)は常に同じです。
3.3 But, there are some disadvantages to create mock object manually like above :
-
最後に、単体テストの目的で、多くのモックオブジェクト(クラス)を作成できます。
-
インターフェイスに多くのメソッドが含まれている場合は、それぞれをオーバーライドする必要があります。
-
まだ手間がかかり、面倒です!
3.4 Solution
Try Mockito, a simple and powerful mocking framework.
4. 単体テスト– 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)); } }
完了しました。 あなたのフィードバックは大歓迎です