플러터 개발 지속을 위한 동기 부여와 재미, 그리고 유닛 테스트의 중요성

2024. 6. 13. 19:17Flutter/Flutter Programming

반응형

개발을 지속하기 위해서는 동기 부여와 재미가 매우 중요합니다.

새로운 기능을 창조하고 테스트하는 과정에서 재미를 느끼지 못한다면 오랫동안 개발하기 어렵습니다.

많은 분들이 초기에는 구현에 집중하다가 시간이 지나면서 테스트와 유지보수의 중요성을 깨닫게 됩니다.

특히, 코드의 양이 많아지면 관리가 어려워지기 때문에 테스트는 필수적입니다.

플러터에서의 테스트 종류

플러터에서 제공하는 테스트는 크게 세 가지로 나눌 수 있습니다:

  1. 유닛 테스트(Unit Test)
  2. 위젯 테스트(Widget Test)
  3. 통합 테스트(Integration Test)

오늘은 이 중 유닛 테스트에 대해 다루겠습니다. 유닛 테스트는 함수, 클래스, 모듈 등의 개별 단위를 테스트하는 것으로, 기능이 잘 동작하는지 확인하는 과정입니다.

유닛 테스트 예제: 카운터 앱

플러터의 기본 카운터 앱을 예제로 사용해 유닛 테스트를 알아보겠습니다. 카운터 앱은 버튼을 눌러 카운터 값을 증가시키는 간단한 앱입니다. 여기에 저장 기능을 추가하여 로컬 스토리지에 값을 저장하고 다시 불러오는 기능을 구현해 보겠습니다.

 
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterApp(),
    );
  }
}

class CounterApp extends StatefulWidget {
  @override
  _CounterAppState createState() => _CounterAppState();
}

class _CounterAppState extends State<CounterApp> {
  int _counter = 0;

  @override
  void initState() {
    super.initState();
    _loadCounter();
  }

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
    _saveCounter();
  }

  void _saveCounter() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setInt('counter', _counter);
  }

  void _loadCounter() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    setState(() {
      _counter = (prefs.getInt('counter') ?? 0);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Counter App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

유닛 테스트 작성

유닛 테스트를 작성할 때는 실제 운영 중인 객체를 그대로 테스트하면 안 됩니다.

운영 중인 서비스에 영향을 줄 수 있기 때문입니다.

이를 해결하기 위해 모킹(Mocking)을 사용합니다. 모킹은 가짜 객체를 만들어 테스트를 진행하는 방법입니다.

 
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:shared_preferences/shared_preferences.dart';

class MockSharedPreferences extends Mock implements SharedPreferences {}

void main() {
  group('CounterApp Tests', () {
    MockSharedPreferences mockPrefs;

    setUp(() {
      mockPrefs = MockSharedPreferences();
    });

    test('increments counter', () {
      // Set initial value
      when(mockPrefs.getInt('counter')).thenReturn(0);

      // Increment counter
      int counter = mockPrefs.getInt('counter') + 1;

      // Verify the increment
      expect(counter, 1);
      verify(mockPrefs.setInt('counter', 1)).called(1);
    });

    test('loads counter value', () {
      // Set mock return value
      when(mockPrefs.getInt('counter')).thenReturn(11);

      // Load counter
      int counter = mockPrefs.getInt('counter');

      // Verify the load
      expect(counter, 11);
    });
  });
}

모킹 패키지 선택

모킹을 도와주는 패키지는 여러 가지가 있지만, 대표적으로 두 가지가 있습니다:

  1. 모키토(Mockito): 다트 팀에서 공식적으로 만들었으며, 안정성이 뛰어나고 기능이 많습니다. 그러나 사용이 복잡할 수 있습니다.
  2. 목테일(Mocktail): 사용이 쉽고, 모키토의 불편함을 개선한 패키지입니다. 그러나 기능이 상대적으로 적고, 개인 개발자가 관리하여 업데이트 주기가 길 수 있습니다.

결론

테스트는 개발 과정에서 매우 중요한 부분입니다. 유닛 테스트를 통해 함수 하나하나의 기능을 검증하고, 모킹을 사용하여 실제 운영 중인 객체에 영향을 주지 않도록 해야 합니다. 모키토와 목테일 중 자신의 취향에 맞는 패키지를 선택하여 사용하면 됩니다.

유닛 테스트를 통해 코드의 안정성을 높이고, 개발의 재미와 동기 부여를 유지하면서 지속적으로 개발을 해 나가시길 바랍니다.

블로그 글과 영상 설명에 포함된 링크를 참고하여 실제 구현을 실습해 보세요. 단계별로 따라가면서 테스트를 진행하면 도움이 될 것입니다. 유닛 테스트에 대한 설명은 여기까지 하겠습니다. 감사합니다.

반응형