Spring FrameworkのUnitテスト実装方法 2-2.Serviceテスト(Junit4, MockClass)

Spring FrameworkのUnitテスト実装方法 2-2.Serviceテスト(Junit4, MockClass)

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

Junitカテゴリの最新記事