Test Driven Development

 Test Driven Development


Testing pada flutter 

1. Unit Test

Test untuk setiap fungsi, method dan class. Tujuan dari test ini adalah untuk memverifikasi kebenaran unit logika dibawah berbagai kondisi. Setelah melakukan refactoring code dan menggunakan bloc pattern, membuat unit test pada code menjadi lebih mudah.

CONTOH CASE TEST BLOC FITUR SEE ALL COMMENTS (COMMENTS LIST) : 

Pertama-tama, perlu dilakukan import package flutter_test, mockito, Comment List Bloc, dan package-package lain yang bersangkutan.


import 'package:bloc/bloc.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:learn_fazz/blocs/comment_list/comment_list.dart';
import 'package:learn_fazz/models/comment_list_result.dart';
import 'package:learn_fazz/repositories/comment_repository.dart';
import 'package:mockito/mockito.dart';

import '../../test_helpers/mock_classes.dart';
import '../../test_helpers/simple_bloc_delegate.dart';
import '../../test_helpers/test_factories/comment_factory.dart';


Pada setUp, buat mock instansi dari CommentRepository menggunakan mockito. Kemudian kami juga menginisiasi CommentListBloc dengan mockCommentRepository sebagai dependensinya.


void main() {
CommentListBloc bloc;
CommentRepository mockCommentRepository;

final CommentListState initialState = GetCommentListSuccess(CommentListResult.empty());

setUp(() {
mockCommentRepository = MockCommentRepository();
bloc = CommentListBloc(commentRepository: mockCommentRepository);
});


Test pertama yang dilakukan adalah sanity test untuk memastikan bahwa ketika dispose dilakukan maka state dari Bloc tidak diupdate.

test('dispose should not emit new states', () {
expectLater(bloc.state, emitsInOrder([]));
bloc.dispose(); });
Untuk melakukan test pada CommentList success, perlu dibuat skenario yaitu untuk pemanggilan fungsi getCommentList akan dihasilkan objects list dari CommentList. Kemudian kami membuat sebuah setup mengenai ekspektasi event apa saja yang dihasilkan oleh Bloc untuk mengetahui apakah comment list sukses dijalankan. Ekspektasi tersebut dideklarasikan pada expectLater dimana salah satu parameternya emitsInOrder berisi pergantian state dari initialState, GetCommentListLoading(), hingga CommentListSuccess(). Pada line terakhir dilakukan bloc dispatch untuk test fungsi yang sudah dibuat.
test('CommentList success', () {
when(mockCommentRepository.getCommentList(any, any)).thenAnswer(
(_) => Future<CommentListResult>.value(
CommentFactory.constructDefaultCommentListResult(),
),
);
final _initialState = initialState;
expectLater(
bloc.state,
emitsInOrder([
_initialState,
GetCommentListLoading(),
GetCommentListSuccess(CommentFactory.constructDefaultCommentListResult())
])).then((_) => bloc.dispose());
bloc.dispatch(GetCommentList(courseId: 1, offset: 0, postId: 1));
});

Test untuk CommentList fail memiliki kode yang hampir sama dengan CommentList success. Perbedaannya terletak pada skenario awal yang melakukan throw 404 error untuk membuat getCommentList fail. Perbedaan lainnya adalah state emitsInOrder terakhir merupakan GetCommentListFailure().

test('CommentList should fail', () {
when(mockCommentRepository.getCommentList(any, any)).thenThrow('404 Error');

expectLater(
bloc.state,
emitsInOrder([
initialState,
GetCommentListLoading(),
GetCommentListFailure('404 Error')
])).then((_) => bloc.dispose());

bloc.dispatch(GetCommentList(courseId: 1, offset: 0, postId: 1));
});
}

MOCK OBJECT

Apa itu mock dan mengapa kita harus menggunakan mock object saat melakukan testing?

Sebuah unit test hanya melakukan test terpisah tergantung dengan fungsionalitasnya. Hal ini berarti kelas atau objek yang digunakan saat melakukan unit test tidak boleh menimbulkan efek lain untuk kelas lainnya yang tidak bersangkutan dengan test tersebut. Contohnya, dalam beberapa kasus, test diperuntukkan bagi kelas yang mengambil data langsung dari live web service atau database. Hal ini termasuk sulit, karena pemanggilan live service atau database dapet memerlambat eksekusi test, dan jika terdapat perubahan pada database atau live web service, akan susah dipastikan apakah test tersebut akan sukses atau gagal. Oleh karena itu, unit test ini dapat dibantu dengan menggunakan test replacement/doubles yang merupakan test pengganti untuk dependensi nyata. Salah satu dari test replacement ini adalah menggunakan mock object.

Mock object adalah implementasi dummy untuk sebuah interface atau class dimana kita menentukan sendiri output dari pemanggilan method tertentu. Objek ini dikonfigurasikan untuk menjalankan suatu behavior pada saat melakukan testing. Mock biasanya dapat merekam interaksi dengan sistem dan test dapat memvalidasi interaksi tersebut. Mock object dapat dibuat secara manual atau menggunakan framework.


2. Widget Test


Test untuk setiap widget. Tujuan dari test ini adalah untuk memverifikasi bahwa UI widget terlihat dan berinteraksi seperti apa yang diharapkan. Dalam project, misalnya widget yang sedang diuji harus dapat menerima respon dari user.

Cara melakukan widget test:

  • Masukkan flutter_test dependency
           dev_dependencies:
      flutter_test:
        sdk: flutter
  • Siapkan widget yang ingin di test
  • Buat sebuah testWidgets test

    void main() {
      // Define a test. The TestWidgets function also provides a WidgetTester
      // to work with. The WidgetTester allows you to build and interact
      // with widgets in the test environment.
      testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
        // Test code goes here.
      });

  • Build widget menggunakan widget tester 

void main() {
  testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
    // Create the widget by telling the tester to build it.
    await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));
  });
}

  • Search widget dengan menggunakan finder

    void main() {
      testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
        await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));
    
        // Create the Finders.
        final titleFinder = find.text('T');
        final messageFinder = find.text('M');
      });
    }

  • Verify widget menggunakan matcher

void main() {
  testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
    await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));
    final titleFinder = find.text('T');
    final messageFinder = find.text('M');

    // Use the `findsOneWidget` matcher provided by flutter_test to verify
    // that the Text widgets appear exactly once in the widget tree.
    expect(titleFinder, findsOneWidget);
    expect(messageFinder, findsOneWidget);
  });
}


3. Integration Test

Test ketika aplikasi telah complete. Tujuan dari test ini adalah untuk memverifikasi kinerja aplikasi yang telah dibuat.

Komentar