본문 바로가기

Flutter

Flutter에서 의존성 주입과 Mockito로 유닛 테스트 작성하기

Flutter 개발을 하다 보면 비즈니스 로직이 잘 동작하는지 검증하고 싶을 때가 많습니다.
이럴 때 가장 좋은 방법이 바로 Unit Test 입니다.
하지만 외부 API 호출(http)이 있는 경우 테스트가 쉽지 않은 경험이 있어서... (이부분은 mockito를 활용하여 처리)

테스트 가능한 구조로 만들고, Mockito를 활용해 유닛 테스트를 작성하는 방법을 정리해보겠습니다.

 

프로젝트 설정

pubspec.yaml에 다음 의존성 추가

dev_dependencies:
  flutter_test:
    sdk: flutter
  mockito: ^5.4.2
  build_runner: ^2.4.6

 

API 추상화와 의존성 주입

먼저 테스트 가능한 구조로 만들기 위해, API 호출 로직을 추상화합니다.

1. 추상 인터페이스 정의

abstract class IRestApi {
  Future<dynamic> doPostWithUser(String apiId, User user, dynamic body);
}

 

-  실제 구현 클래스

import 'dart:convert';
import 'package:http/http.dart' as http;

class RestApiWrapper implements IRestApi {
  final http.Client client;

  RestApiWrapper(this.client);

  @override
  Future doPostWithUser(String apiId, User user, body) async {
    final headers = {
      "Content-type": "application/json",
      "X-COG-TOKEN": user.userToken!,
    };

    final response = await client.post(
      Uri.parse('https://example.com/$apiId'),
      headers: headers,
      body: json.encode(body),
    );

    if (response.statusCode == 200) {
      return response.body.isNotEmpty ? json.decode(response.body) : true;
    } else {
      return false;
    }
  }
}

 

 

서비스 클래스에서 인터페이스 주입

class TestService {
  final IRestApi api;

  TestService(this.api);

  Future<dynamic> sendData(User user, dynamic body) {
    return api.doPostWithUser('homeappliance', user, body);
  }
}

 

테스트 코드 작성

mock 클래스 생성

test/mock_api.dart (경로)

import 'package:mockito/annotations.dart';
import 'package:your_project_path/service/irest_api.dart';

@GenerateMocks([IRestApi])
void main() {}

 

이렇게 하고 code generator를 실행

flutter pub run build_runner build

 

 

실제 테스트 코드

import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:your_project_path/service/test_service.dart';
import 'package:your_project_path/service/irest_api.dart';
import 'mock_api.mocks.dart';

void main() {
  group('TestService', () {
    late MockIRestApi mockApi;
    late TestService service;

    setUp(() {
      mockApi = MockIRestApi();
      service = TestService(mockApi);
    });

    test('샘플 테스트', () async {
      final user = User()..userToken = 'mock_token';
      final body = {'key': 'value'};
      final mockResponse = {'result': 'ok'};

      when(mockApi.doPostWithUser('homeappliance', user, body))
          .thenAnswer((_) async => mockResponse);

      final result = await service.sendData(user, body);

      expect(result, mockResponse);
      verify(mockApi.doPostWithUser('homeappliance', user, body)).called(1);
    });
  });
}

 

- mockResponse는 실제 API 호출 성공시 예상하는 response, result는 sendData 내부에서 mockResponse를 리턴하는 결과값

- expect를 통해 예상하는 결과값을 인자로 넣어서 테스트 성공 여부 판단하는 구조.