Skip to content

Number trivia app from Reso Coder's tutorial (2019) working with null safety. (New update coming soon)

Notifications You must be signed in to change notification settings

farvic/tdd-clean-number-trivia-app

Repository files navigation

Test-Driven Development with Clean Architecture

This repository is based on Reso Coder's awesome TDD Clean Architecture Course, which you can see by clicking here or clicking here to go to the original repo.

Since the tutorial was made in 2019, a lot of things changed. Now the new versions of flutter are null safety, what made some of the old versions' code deprecated. Additionally some of the libraries used on the original tutorial were discontinued, like the Data Connection Checker.

While going through the tutorial, I've updated some of the old code to make it work on the new versions of flutter. Some library choices were made, like the Mockito in favor of Mocktail due to it making testing with null safety work closer to what's in the tutorial without the need to generate more complicated code.

Updated and changed libraries

The libraries were updated to the latest version, except for the flutter_bloc, which was updated to v7.2.0, the last null safety version before the changes on how to Handle events, because I wanted to keep the code as close as possible to the old version to make it easier to follow the tutorial. Additionally, some libraries were removed in favor of others. which is the case for using mocktail instead of mockito. More details below.

  get_it: ^7.2.0
  flutter_bloc: ^7.2.0
  equatable: ^2.0.3
  dartz: ^0.10.1
  http: ^0.13.4
  shared_preferences: ^2.0.15
  mocktail: ^0.3.0
  internet_connection_checker: ^0.0.1+4

Mocktail instead of Mockito

Mockito's any argument doesn't work very well with null safety due to being illegal to pass null where a non-nullable variable is expected. Mockito's workaround for this problem can be seen by going to its readme clicking here. Since it would deviate a lot from the original tutorial, I decided to go with Mocktail instead. Check the example below from the number_trivia_repository_impl_test.dart:


A test on the old version original code (with mockito):

test(
    'should return server failure when the call to remote data source is unsuccessful',
    () async {
        // arrange
        when(mockRemoteDataSource.getConcreteNumberTrivia(any))
            .thenThrow(ServerException());
        // act
        final result = await repository.getConcreteNumberTrivia(tNumber);
        // assert
        verify(mockRemoteDataSource.getConcreteNumberTrivia(tNumber));
        verifyZeroInteractions(mockLocalDataSource);
        expect(result, equals(Left(ServerFailure())));
    },
);

Is gonna look like this (with mocktail):

test(
    'should return server failure when the call to remote data source is unsuccessful',
    () async {
        // arrange
        when(() => mockRemoteDataSource.getConcreteNumberTrivia(any()))
            .thenThrow(ServerException());
        // act
        final result = await repository.getConcreteNumberTrivia(tNumber);
        // assert
        verify(() => mockRemoteDataSource.getConcreteNumberTrivia(tNumber));
        verifyZeroInteractions(mockLocalDataSource);
        expect(result, equals(Left(ServerFailure())));
    },
);

The only changes where the when, the any and the verify.


InternetConnectionChecker instead of DataConnectionChecker

Since the data_connection_checker was discontinued and no longer worked, I decided to go with the internet_connection_checker library. Everything that has the type DataConnectionChecker becomes InternetConnectionChecker and we are done.


Uninitialized variables

To avoid certain runtime errors, null safety requires you to declare non-nullable uninitialized variable with the late keyword to tell the program that the variable will be initialized later on. This change is present in all the test files, like this example at the beginning of get_concrete_number_trivia_test.dart:

  late GetConcreteNumberTrivia usecase;
  late MockNumberTriviaRepository mockNumberTriviaRepository;

  setUp(() {
    mockNumberTriviaRepository = MockNumberTriviaRepository();
    usecase = GetConcreteNumberTrivia(mockNumberTriviaRepository);
  });

Bloc streams and testing

When testing for bloc states (number_trivia_bloc_test.dart), you should send a bloc.stream instead of the actual bloc as the first argument when calling expectLater:

    test(
      'should emit [Error] when the input is invalid',
      () async {
        // arrange
        when(() => mockInputConverter.stringToUnsignedInteger(any()))
            .thenReturn(Left(InvalidInputFailure()));
        // assert later
        final expected = [
          Error(message: INVALID_INPUT_FAILURE_MESSAGE),
        ];
        expectLater(bloc.stream, emitsInOrder(expected));
        // act
        bloc.add(const GetTriviaForConcreteNumber(tNumberString));
      },
    );

Instead of what's in the original code. Additionally, you're not gonna need to provide the Empty() state as part of the expected states since it's already being tested right before the other tests.

  test('initialState should be Empty', () {
    // assert
    expect(bloc.initialState, equals(Empty()));
  });

App theming

Now the app theme is gonna be defined like this in the main.dart:

  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Number Trivia',
      theme: theme.copyWith(
        colorScheme: theme.colorScheme.copyWith(
          primary: Colors.green.shade800,
          secondary: Colors.green.shade600,
        ),
      ),
      home: const NumberTriviaPage(),
    );
  }



I believe that the main changes were covered here. If there's anything left, feel free to contact me.

About

Number trivia app from Reso Coder's tutorial (2019) working with null safety. (New update coming soon)

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published