Spring FrameworkのUnitテスト実装方法 2-2.Serviceテスト(Junit4, MockClass)
TERASOLUNA Server Framework for Java (5.x) Development Guideline
サンプルソースはこちら
(これのMyBatis3を使用したパターンで作成してます。)
2-2.Serviceテスト Junit4ライブラリを使用したパターン
Serviceクラスのテストです。
Repositoryクラスのメソッドをモック化してテストしています。
モック用にクラスを新たに作成して、そのメソッドに置き換えるという流れです。
【pom.xml】
Junitライブラリが必要です。
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> </dependencies>
【テスト対象クラス】
TodoServiceImpl.java
テスト対象はfinishメソッド
@Service @Transactional public class TodoServiceImpl implements TodoService { private static final long MAX_UNFINISHED_COUNT = 5; @Inject TodoRepository todoRepository; public Todo findOne(String todoId) { Todo todo = todoRepository.findOne(todoId); if (todo == null) { ResultMessages messages = ResultMessages.error(); messages.add(ResultMessage.fromText("[E004]The requested Todo is not found. (id=" + todoId + ")")); throw new ResourceNotFoundException(messages); } return todo; } @Override public Todo finish(String todoId) { // TODO 自動生成されたメソッド・スタブ Todo todo = findOne(todoId); if (todo.isFinished()) { ResultMessages messages = ResultMessages.error(); messages.add(ResultMessage.fromText("[E002]The requested Todo is already finished. (id=" + todoId + ")")); throw new BusinessException(messages); } todo.setFinished(true); todoRepository.update(todo); return todo; } }
モック化の対象メソッドは、todoRepository.updateメソッドとtodoRepository.findOneメソッドになります。
今回のテスト対象クラスのfinishメソッドでは内部メソッドのfindOneメソッドを呼んでいるのですが、
内部メソッドもモック化して純粋にfinishメソッドのみのテストがしたいという場合はPowerMockとか使って無理やりモック化する方法があります。でも今回はやりません。
【テストクラス】
TodoServiceImplTestVerMockClass.java
public class TodoServiceImplTestVerMockClass { TodoServiceImpl target; @Before public void setUp() throws Exception{ //テストメソッドでモックを切り替えるため処理なし } //正常に動作したパターン @Test public void testFinishOK() throws Exception{ //モッククラスの適用(TestFinishOKMock) target = new TodoServiceImpl(); TodoRepository mockTodoRepository = new TestFinishOKMock(); target.todoRepository = mockTodoRepository; //引数設定 String todoId = "cceae402-c5b1-440f-bae2-7bee19dc17fb"; //finishメソッドのテスト Todo todo = target.finish(todoId); //結果検証(assertTodoメソッドはメソッドの実行によって返ってきたTodoオブジェクトを検証するメソッド) assertTodo(todo); } //取得したTodoオブジェクトのfinishedが既にtrueで異常が発生したパターン //@Testのexpectedには期待するExceptionクラスを設定する。こうすることでExceptionが発生することは想定通りということを宣言している。 @Test(expected = BusinessException.class) public void testFinishNG() throws Exception{ //モッククラスの適用(TestFinishNGMock) target = new TodoServiceImpl(); TodoRepository mockTodoRepository = new TestFinishNGMock(); target.todoRepository = mockTodoRepository; //引数設定 String todoId = "cceae402-c5b1-440f-bae2-7bee19dc17fb"; //try-catch文はなくてもJunitとしては正常になるが、printStackTraceメソッドでエラーの内容を表示させている。 try { target.finish(todoId); }catch (BusinessException e) { // TODO: handle exception e.printStackTrace(); throw e; } } }
想定通りにデータを取ってきた時のパターンと、
異常が発生した時のパターンの2パターンを記述しました。
期待通りの異常が発生した場合は @Test(expected = Exceptionクラス)と設定することで、Junitのテストが成功になります。
【その他設定】
TestFinishOKMock.java
testFinishOK()メソッドでテストする対象のモックです。
public class TestFinishOKMock implements TodoRepository { //finishedにfalseが入ったデータを返すモック @Override public Todo findOne(String todoId) { Todo todo = new Todo(); todo.setTodoId("cceae402-c5b1-440f-bae2-7bee19dc17fb"); todo.setTodoTitle("one"); todo.setFinished(false); try { SimpleDateFormat sdFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); String strDate = "2017-10-01 15:39:17.888"; Date date1 = sdFormat.parse(strDate); todo.setCreatedAt(date1); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } return todo; } //trueを返すモック @Override public boolean update(Todo todo) { return true; } }
TestFinishNGMock.java
testFinishNG()メソッドでテストする対象のモックです。
public class TestFinishNGMock implements TodoRepository{ @Override public Todo findOne(String todoId) { Todo todo = new Todo(); todo.setTodoId("cceae402-c5b1-440f-bae2-7bee19dc17fb"); todo.setTodoTitle("one"); todo.setFinished(true); try { SimpleDateFormat sdFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); String strDate = "2017-10-01 15:39:17.888"; Date date1 = sdFormat.parse(strDate); todo.setCreatedAt(date1); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } return todo; } //trueを返すモック @Override public boolean update(Todo todo) { return true; } }
あらかじめfinishメソッド対象のデータをtrueとしておくことで異常を発生させる。
まーシンプルですよね。Springの機能一切関係なくて純粋にjavaのクラスそのもののテストをしているって感じです。
サンプルソースはgithubで公開してます。
Spring Unit Test pattern9