말랑한 하루

[Flutter] TDD (Test Driven Development) - UseCase 본문

개발/Flutter

[Flutter] TDD (Test Driven Development) - UseCase

지수는말랑이 2024. 2. 7. 14:10
반응형

 

Clean Architecture에서 UseCase는 Repository를 활용하여 실질적인 비즈니스 로직을 구현하는 것에 초점을 맞추고 있습니다. UseCase Test는 결과를 잘 가져오는지 그리고 MockRepository를 활용한 결과 값과 UseCase의 결과 값이 같은지 비교합니다.

🍒 객체 선언 및 초기화

class MockTodoRepository extends Mock implements TodoRepository {}

void main() {
  late TodoUseCase todoUseCase;
  late MockTodoRepository mockTodoRepository;

  setUp(() {
    mockTodoRepository = MockTodoRepository();
    usecase = TodoUseCase(mockTodoRepository);
  });
	
	...
}

🍒 더미 데이터 생성

UseCase의 결과 값에 대한 더미 데이터를 생성합니다.

final List<Todo> testTodos = [
  Todo(id: 1, title: 'Todo 1'),
  Todo(id: 2, title: 'Todo 2'),
];

🍒 테스트 선언/준비/실행/검증

🍇 테스트 선언

테스트 이름을 설정합니다. 해당 테스트가 어떤 동작을 검증하는지 쉽게 이해할 수 있도록 작성해야 합니다.

test('should get a list of Todo entities from the repository', () async {});

🍇 테스트 준비/실행/검증

테스트는 arrange/act/assert 즉, 준비/실행/검증 단계로 구조화되어 작성됩니다. 코드의 가독성을 높이고 어떤 부분에서 테스트가 실패했는지 명확하게 파악할 수 있습니다.

특히 여러 테스트 케이스를 작성할 때 일관성을 유지하고 테스트의 목적을 명확히 전달하는 것에 도움을 줄 수 있습니다.

test('should get a list of Todo entities from the repository', () async {
  // arrange
  when(mockTodoRepository.getTodoList()).thenAnswer((_) async => testTodos);

  // act
  final result = await todoUseCase(NoParams());

  // assert
  expect(result, equals(testTodos));
  verify(mockTodoRepository.getTodoList());
});

초반에 설계 했던 테스트 목적에 대한 각 arrange/act/assert는 다음과 같은 모습으로 설계할 수 있습니다.

 

🍌 arrange

Repository Mock 객체를 활용하여 결과 값을 수신합니다. 결과 값은 “thenAnswer" 메소드를 활용해 생성해 논 더미데이터로 대체합니다.

🍏 thenAnswer

thenAnswer 메소드는 mockito 라이브러리에서 제공하는 기능입니다.

 

🍌 act

todoUseCase를 활용한 결과 값을 생성합니다.

 

🍌 assert

mock 객체를 활용하였으므로 mockito 라이브러리의 "verify" 메소드를 활용해 mock 객체가 정확히 동작하는지 확인합니다. 또한, 두 결과 값이 일치하는지 비교하기 위해 flutter_test 라이브러리의 "expact" 메소드를 활용합니다.

 

🍇 테스트 그룹

여러개의 테스트를 그룹화 할 수 있습니다. 관련된 테스트 케이스를 묶어서 Test Suite를 형성하고, 코드의 특정 부분이나 기능에 대한 다양한 측면을 검증하기 용이하게 만들어줍니다.

 

보통 가독성 향상/유사한 행위에 대한 통합 검증/효율적인 테스트 수행/코드 수정 용이성에 대한 이점을 얻을 수 있습니다.

 

"group" 메소드를 활용하고 전반적인 테스트를 통합하여 표현할 수 있는 문구 또는 함수명을 작성하는 것이 보편적입니다.

 

전체 소스코드는 다음과 같습니다.

class MockTodoRepository extends Mock implements TodoRepository {}

void main() {
  late TodoUseCase todoUseCase;
  late MockTodoRepository mockTodoRepository;

  setUp(() {
    mockTodoRepository = MockTodoRepository();
    usecase = TodoUseCase (mockTodoRepository);
  });

  group('GetTodoList', () {
    final List<Todo> testTodos = [
      Todo(id: 1, title: 'Todo 1'),
      Todo(id: 2, title: 'Todo 2'),
    ];

    test('should get a list of Todo entities from the repository', () async {
      // arrange
      when(mockTodoRepository.getTodoList()).thenAnswer((_) async => testTodos);

      // act
      final result = await todoUseCase(NoParams());

      // assert
      expect(result, equals(testTodos));
      verify(mockTodoRepository.getTodoList());
    });

    test('should throw an exception if the repository call fails', () async {
      // arrange
      when(mockTodoRepository.getTodoList()).thenThrow(Exception('Repository error'));

      // act
      final call = usecase;

      // assert
      expect(() => call(NoParams()), throwsException);
      verify(mockTodoRepository.getTodoList());
    });
  });
}

🍒 테스트 진행

테스트는 루트 디렉토리에서 다음 명령어를 활용하여 실행할 수 있습니다.

flutter test {filePath}/{fileName}.dart

각 테스트 케이스에 대한 결과와 통과 여부에 대한 정보가 터미널에 출력됩니다.

00:10 +3: All tests passed!
00:10 +3 -1: Some tests failed.

테스트 결과는 시간/개수/내용으로 구분되어 표기되며 성공한 테스트는 "+", 실패한 테스트는 "-"로 표기됩니다.

 

테스트 코드에서 print 함수를 활용하여 명시적으로 작성한 내용과 함께 실패의 원인이 되는 에러 메세지가 터미널에 표기되므로, 참고하여 테스트 준비/실행/검증 단계를 반복해가면 됩니다.

반응형
Comments