Test Code Sugaring - Using types and code extensions to improve code expressiveness
Tags: dart, testing, architecture, flutter
Published: 2020-08-15 22:07:58

All of this is applicable of any typed language, I used it in android with Kotlin in Android and now Im happy to use them in Flutter also (the at the bottom of the post to see an example of how you can do it on android and on dart).

# Without types (be sad)

Lets assume that you are testing a presenter like this:

```dart
class LoginPresenter {
    final userRepository = UserRepository();

    void onLogin(String email, String password) async {
        final response = userRepository.login(email, password);
        
        if(response.httpCode == 200) {
            // Parse the response body and do something with it
        } else {
           // parse the response body to get the errors
        }
    }
}
```

That is using some abstractions like this ones:
```dart
class Response {
    String body;
    int httpCode;
}

class UserRepository {
    Future<Response> login(String email, String password) { 
      // Do the Http call here and return the http response
    }
}
```

## How your tests may look
Using mockito to create the mocks:

```dart
void main() {
    test('on success', () {
       whenever(userRepository.login(any, any)).thenAnswer(() => Future.value(Response("your big json response", 200)));
       presenter.onLogin("email", "password");

       // expect each thing based on the json response values
    }) 
}
```

## Problems with this

The problems with this kind of tests are:
- You should be testing only 1 thing in a unit test, with this, you are testing the JSON parsed object, think the case that the service you are using changed the representation of the response to something else, you would have to go and change every BIG json string file and pray :P.
- There are no so much separation of concerns since the presenter know what you are using Http to fetch the data, also know that 200 is a success, what if the service starts answering with 201? 
- You will end up with big Json strings representing each possible state that the presenter will handle and that your service may answer. *You will suffer maintaining this JSONs over the time*.
- If you have to mock multiple repositories it is a little bit fill of boilerplate like returning a `Future.value` that is inside a function `() => ...` and the 200 again everywhere.
- The presenters should parse the JSON file to get the errors and handle each case, and sometimes it requires you to use the http code together.

All of this is a lot of irresponsibility for a presenter doesn't it? when something does a lot of things then testing and maintaining those tests is ~~a hell~~ *really hard*.

# Adding types for the rescue!
You can add some types to your system to represent the Success and the Failure of any task everywhere.

You can add some simple abstractions:

```dart
abstract class Result<T> {
}

class Success<T> extends Result<T> {
  final T body;

  Success(this.body);
}

class Failure<T> extends Result<T> {
  final String cause;

  Failure({this.cause});
}
```

I

And a repository that returns responses with `Result` class, like:

```dart
class Session {
   int userId;
}

class UserRepository {
    Future<Result<Session>> login(String email, String password) { 
      // Do the Http call here and get the user id...
    }
}
```

Now the presenters will look a cleaner:

```dart
class LoginPresenter {
    final userRepository = UserRepository();

    void onLogin(String email, String password) async {
        final result = userRepository.login(email, password);
        
        if (result is Success<Session>) {
           // use result.body.userId been sure that it is an int
        } else if (result is Failure<Session>) {
           // use result.message been sure that theres a string with the error
        }
    }
}
```

## Goods of this

- You remove a lot of knowledge from presenters, they will not know anything about Http, http codes or json parsing.
- You defined types for responses an so presenters can be sure that the response is a `Session` that have an int attribute with the userId, so no double check or casting from string to the type you are supposed to get.

# How tests benefits from this
Now that you know that your repositories responses your types, you can add extensions like this:

```dart
extension ResultMock<T> on PostExpectation<Future<Result<T>>> {
  // One for success
  void thenSucceed(T body) {
    var result = Result.success(body);
    thenAnswer((realInvocation) => Future.value(result));
  }

  // One for failures
  void thenFail([String message = 'mock-error']) {
    var result = Failure(message);
    thenAnswer((realInvocation) => Future.value(result));
  }
}
```

Then the tests may look like:

```dart
void main() {
    test('on success', () {
       whenever(userRepository.login(any, any)).thenSucceed(Session(1));
       presenter.onLogin("email", "password");

       // expect each thing based on the json response values
    }) 
}
```

## What you get from this
- Tests are testing only one thing and that's the behaviour of the presenter.

- No need to maintain JSONS everywhere, you can add tests in the repository to test the parsing if that's what you want to, but they will be in only one place of your system.

- You will mock easily everywhere since you don't need to think that you need to create a Future, a lambda that answers that future and the proper result type, just use the extension with the response type.

- Some people prefer to use fake implementations of their abstractions to "mock responses", that is not the topic of this post to' I believe that with this you may not need to create fake implementations, mocking became so easy with this...

- This can be used on any typed language, here are some examples of `dart` and `kotlin` extensions if you come from android and want to try it:

{% gist https://gist.github.com/danielgomezrico/4a519ff8bbd57250b60fa761559772ea %}


Types are a really powerful thing to separate concerns of your system.