# Making classes testable

Sometimes classes can be difficult to test. In this article I will show you a way to make classes testable.

*This article is the fifth in the [Introduction to TDD](https://fluttergamedev.com/series/introduction-to-tdd) series where we are creating Tic Tac Toe game logic using Test Driven Development. In the [previous article](https://fluttergamedev.com/writing-your-first-test-in-dart) we've written tests and implementation for one of the properties. In this article we'll continue with the next.*

# The status property

In this article we'll focus on the `status` property of our `TicTacToeGameState` class. In the previous article we've given it a temporary implementation to keep the compiler happy, but it's far from useful.

```dart
Status get status => throw 'not implemented';
```

As the return type tells us, this *getter* should be returning a `Status`. We'd defined `Status` as an `enum`.

```dart
enum Status { p1Turn, p2Turn, p1Win, p2Win, draw }
```

We should be returning one of these values based on the state of the game, which is actually just the `fields`.

# The initial status

Just like with the `fields` *getter* we should decide on what to return as the initial value. So when `fields` is *empty*, what should we return from `status`?

Well, definitely not `.p1Win`, `.p2Win` or `.draw` since the game should not be over yet. So it should be either `.p1Turn` or `.p2Turn`. We didn't really have a *rule* for this, so let's decide that it's always player 1 that starts, and thus should be returning `Status.p1Turn`.

## Writing the test for the initial status

Let's write the test for our new *rule* where player 1 always is the first to claim a field.

```dart
void main() {

  // pretend the fields tests are here

  group(
    'status',
    () {
      test(
        'initial state should return p1Turn',
        () {
          final gameState = TicTacToeGameState();

          expect(
            gameState.status,
            Status.p1Turn,
          ); 
        },
      );
    },
  ); 
}
```

Again we'll create a new group for this property. We create a new test that creates a new game state and we check if the `.status` *getter* returns `Status.p1Turn`.

## Running our initial state test

When we run the test we'll expect it to fail, which it does:

```none
$ dart test --name="initial state should return p1Turn"
00:01 +0 -1: test/test_test.dart: status should return p1Turn [E]                                                                                                              
  not implemented
  test/test_test.dart 11:24  TicTacToeGameState.status
  test/test_test.dart 43:21  main.<fn>.<fn>
  
00:01 +0 -1: Some tests failed.
```

Our test fails because an *error* is being thrown. We'll not really an error, just a `String`, but you get the idea. Tests usually fail because the outcome does not match the expected outcome. Dart will tell you what was the actual value versus the expected one.

In this case we simply see the 'not implemented' `String` being shown in the output.

## Implementing the initial status

Just like we did with the initial value for `fields` we can easily satisfy the test by returning what is expected. So let's update our `status` getter:

```dart
enum Player { one, two }

enum Status { p1Turn, p2Turn, p1Win, p2Win, draw }

class TicTacToeGameState {

  List<Player?> get fields => [null,null,null,null,null,null,null,null,null];
  Status get status => Status.p1Turn;

  TicTacToeGameState claimField(int index) => throw 'not implemented';
}
```

So now `status` will always return `Status.p1Turn`, and that's good enough for now. It's the minimum amount of implementation needed for the test to pass. See for yourself:

```none
dart test --name="initial state should return p1Turn"
00:00 +1: All tests passed!   
```

# Modifying our class for testing

We currently only have a test for one of five possible outcomes. The outcomes of `status` are related to `fields`. So in order to properly test `status` we need a way to *change* `fields`. And there are a few ways to do this...

## Ways to change fields

There are two obvious ways to come to states that we can use for our tests.

### Update the constructor

One way to get a `TicTacToeGameState` with the right `fields` needed for testing is to simply update the constructor to accept an entry for `fields` so we could do something like this:

```dart
final testFields = [Player.one,null,null,null,null,null,null,null,null];
final testState = TicTacToeGameState(fields: testFields);
```

This could work. But remember that our goal is to cover the whole public API and we've just made it more complicated. The consumer of our package could also use this constructor to create a `TicTacToeGameState`.
But what if they add a `List` that has more (or less) than nine entries? Or that it represents an impossible state of Tic Tac Toe that could never happen.
If we decided to take this approach, a lot more tests and a lot more logic should be created to cover these cases.

### Playing the game

An other option to get to the state that we want to test is by implementing `claimFields` and *play* the game until we reach the state we need. But if we'd do that, we'd be testing both `claimFields` and `status`. I think it's better to separate these tests so they're not depending on each other.

## Seed constructor

So actually, the first solution of having a constructor to accept some `fields` is perfect, except for the API being public part of course!

Luckily there is something that we can do about this. We can use the [meta](https://pub.dev/packages/meta) package. The description of the package is as follows:

> This package defines annotations that can be used by the tools that are shipped with the Dart SDK.

One of those annotations can be used to solve our problem. I'm talking about the `@visibleForTesting` annotation. By annotating a public *property*, *method* or *constructor* we tell Dart it should only only be... indeed... available for testing.

This way we don't touch the public API so we don't add unnecessary options for the consumer to worry about and don't create more test work for ourselves.

To use this package, simply add it to the `pubspec.yaml`.

### Adding the constructor

What we will do is create another [named constructor](https://dart.dev/guides/language/language-tour#named-constructors) that we can use to *seed* a value for `fields`.

This is going to change the internals of our `TicTacToeGameState` quite a bit since we now have to store these seeded `fields` as well.
When we apply this change it will look like this:

```dart
class TicTacToeGameState {
  final List<Player?> fields;

  TicTacToeGameState()
      : fields = const [null, null, null, null, null, null, null, null, null];

  @visibleForTesting
  TicTacToeGameState.seed({required this.fields});

  Status get status => Status.p1Turn;

  TicTacToeGameState claimField(int index) => throw 'not implemented';
}
```

As you can see, we've changed the `fields` getter into a *final* (read-only) property. Underneath you'll see not one, but two constructors. The first constructor replaces the [default constructor](https://dart.dev/guides/language/language-tour#default-constructors). We need to make it explicit because we have to initialize our new `fields` property.

The second constructor is our new seed constructor which accepts a value for `fields` and is annotated with the `@visibleForTesting` annotation.

### Sanity check

After such a HUGE refactor, it's good to check if we haven't broken anything. Our goal was to not impact the public API after all. So let's run all our tests:

```none
$ dart test
00:00 +1 -1: changing the returned list should not alter the inner state [E]
  Unsupported operation: Cannot modify an unmodifiable list
  dart:_internal             UnmodifiableListMixin.[]=
  test/test_test.dart 36:23  main.<fn>
  
00:00 +2 -1: Some tests failed.  
```

Oh, one of our tests is failing! The test that now fails is the one we added to prevent the possibility of editing the internal state, AKA the `fields`. It tells us that it tried to modify it. It didn't work since the `List` is unmodifiable (because it's `const`), so it threw an error.

### Returning a copy of fields

Our previous implementation was a simple getter that just returned a new `List` every time it got called. Now we're returning the `fields` that are stored. To pass the test we should again return a `List` that can be modified but doesn't change the internal state.

So all we need to do is return a copy:

```dart
class TicTacToeGameState {
  final List<Player?> _fields;

  TicTacToeGameState()
      : _fields = const [null, null, null, null, null, null, null, null, null];

  @visibleForTesting
  TicTacToeGameState.seed({required List<Player?> fields}) : _fields = fields;

  Status get status => Status.p1Turn;
  
  List<Player?> get fields => List.from(_fields);

  TicTacToeGameState claimField(int index) => throw 'not implemented';
}
```

To be able to return a copy we reintroduced the `fields` getter. The `fields` property we've turned into a private property (recognizable by the `_`). The constructors now set this private property (`_fields`) and the `fields` getter returns a copy every time it gets called.

Now we run the test once more:

```none
$ dart test
00:00 +3: All tests passed!  
```

# Testing all possible outcomes

With our new `.seed` constructor we are able to write the tests for the other outcomes of `status`. So let's get on with the next one!

## You're up, player 2!

Let's now create a test that checks for the turn of player 2. So we basically are going to cover the following rule that we've written down during our [preparation](https://fluttergamedev.com/preparing-for-development).

> Players, in turn, claim a field by marking it (usually 'X' for player 1 and 'O' for player 2).

### Creating the test

Tic Tac Toe is turn-based, which simply means the players 1 and 2 alternate turns. So to test this we should create a situation in which player 2 has to make a move.
The easiest test would be to create a situation where only player 1 has made a move, like this:

![Player two turn](https://cdn.hashnode.com/res/hashnode/image/upload/v1657826922588/EEpg8DgEE.png align="left")

Here player 1 has claimed the top left field. Now it's player 2's turn to make the next claim.
Let's create this same situation as a test in our `status` group:

```dart
test(
  'should return p2Turn',
  () {
    final testFields = [
      Player.one,
      null,
      null,
      null,
      null,
      null,
      null,
      null,
      null
    ];
    final gameState = TicTacToeGameState.seeded(fields: testFields);

    expect(
      gameState.status,
      Status.p2Turn,
    );
  },
);
```

This test is pretty similar to the first test we've created for `status`. Only this time we use our `.seed` constructor to pass in a `List` of `fields`. The first entry of this `List` is set to `Player.one`, because this is the top-left field:

![Fields indexes](https://cdn.hashnode.com/res/hashnode/image/upload/v1657828229732/ohGy3NzLT.png align="left")

### Running the test

Running the test now should result in a fail:

```none
$ dart test --name="should return p2Turn"
00:01 +0 -1: test/test_test.dart: status should return p2Turn [E]                                                                                                              
  Expected: Status:<Status.p2Turn>
    Actual: Status:<Status.p1Turn>
  
  package:test_api          expect
  test/test_test.dart 74:9  main.<fn>.<fn>
  
00:01 +0 -1: Some tests failed.
```

The test fails because it expects a `.p2Turn` but the actual value is `.p1Turn`.

### Passing the test

Let's quickly resolve this by updating the `.status` getter!

```dart
Status get status => _fields[0] == Player.one ? Status.p2Turn : Status.p1Turn;
```

We add a simple check if the first field has been claimed by player 1 and return `.p2Turn` if that's the case, else, return `.p1Turn`.

```console
$ dart test --name="should return p2Turn"
00:01 +1: All tests passed! 
```

And that's it. We've satisfied another test.

## Writing proper implementations

Okay, I agree, that was kind of lame. If we went on like this we'd end up with writing tests for all possible states, and then solving them almost hard-coded. This kind of repetitive work is **not** what we programmers do, right?

What I like to do in this situation is to write multiple tests for the same *rule* with different situations. So instead of testing one situation where it's player 2's turn, we can write three tests for example.
Then we will try to solve this rule (so all tests at once) with a proper solution, instead of hard-coding them.

### Picking two more situations

So to do this we will create two more situations in which player 2 is allowed to make a move. I've selected the following two:


![Schermafbeelding 2022-08-04 om 21.00.40.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1659639786544/7v5ilqkRT.png align="left")

![Schermafbeelding 2022-08-04 om 21.02.19.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1659639797995/iWeqHJtul.png align="left")

In both cases player 1 was the last player that made a move. The game is also not yet finished (no one has claimed three fields in a row and not all fields have been claimed).
This means that it's up to player 2 to claim the next field.

So let's turn these situations into tests.

### Adding the tests

Adding these new tests is easy. We simply copy-paste the previous one, update the `List` we seed into our new `.seed` constructor and we're done!

We will end up with a  `group` for the `status` *getter* looking like this:

```dart
group(
    'status',
    () {
      test(
        'should return p1Turn',
        () {
          final gameState = TicTacToeGameState();

          expect(
            gameState.status,
            Status.p1Turn,
          );
        },
      );

      test(
        'should return p2Turn',
        () {
          final testFields = [
            Player.one,
            null,
            null,
            null,
            null,
            null,
            null,
            null,
            null,
          ];
          final gameState = TicTacToeGameState.seeded(fields: testFields);

          expect(
            gameState.status,
            Status.p2Turn,
          );
        },
      );

      test(
        'should return p2Turn',
        () {
          final testFields = [
            Player.one,
            null,
            Player.two,
            null,
            Player.one,
            null,
            null,
            null,
            null,
          ];
          final gameState = TicTacToeGameState.seeded(fields: testFields);

          expect(
            gameState.status,
            Status.p2Turn,
          );
        },
      );

      test(
        'should return p2Turn',
        () {
          final testFields = [
            null,
            Player.two,
            null,
            Player.two,
            Player.one,
            Player.one,
            null,
            null,
            Player.one,
          ];
          final gameState = TicTacToeGameState.seeded(fields: testFields);

          expect(
            gameState.status,
            Status.p2Turn,
          );
        },
      );
    },
  );
```

Great, let's run the tests!

### Running all the tests for player 2's turn.

As you might have noticed is that we did not change the names of the new tests. This allows us to run all these tests at once!
Let's do it:

```none
$ status should return p2Turn [E]                                                                                                              
  Expected: Status:<Status.p2Turn>
    Actual: Status:<Status.p1Turn>
  
  package:test_api           expect
  test/test_test.dart 122:11  main.<fn>.<fn>
  
00:00 +2 -1: Some tests failed.
```

You'll see that all the three tests ran and one of them failed.
So we'll have to update the implementation to solve this.

### Fixing the implementation

One of the reasons I like to create and solve multiple tests at once in these situations is that you might already discover some patterns when creating the test cases.

As you know, Tic Tac Toe is a turn-based game. While writing the tests for player 2's turn, you might have noticed that this is reflected by the fact that every time we *seed* a `List`, it will contain one more `Player.one` entry than `Player.two` entries.
This is a clue we can use for the new implementation!

When we apply this, our `status` getter could look something like this:

```dart
Status get status {
  final p1Count = _fields.where((field) => field == Player.one).length;
  final p2Count = _fields.where((field) => field == Player.two).length;
  return p1Count == p2Count ? Status.p1Turn : Status.p2Turn;
}
```

We simply get the amount of fields claimed by each player and check if they are equal.
If they are equal, it means that player 2 has made the last claim and thus it's player 1's turn. If they are not equal, it means that player 1 has claimed one more field then player 2, and thus it's player 2's turn.

### Re-running the tests

Now if we re-run the tests you'll see that they'll pass:

```none
$ dart test --name="should return p2Turn"
00:00 +3: All tests passed!
```

# What's next?

I've showed you a way to make a class testable without relying on other functionality of the class that's being tested and without impacting the public API by using the `@visibleForTesting` annotation from the `meta` package. With it we added a new constructor that allows us to put the `TicTacToeGameState` in the state we need it to be for writing our tests.

We've used this to write tests for the `status` getter that should return `.p2Turn` when it's... player 2's turn.

But as you might have noticed we're copy-pasting quite a bit. Just these three simple tests we've added make up for 85 lines of code. This doesn't make the file very maintainable and readable.

In the next article I will show you how we can use our developer skills to improve our tests!

**Next article: [Using your dev skills for testing](https://fluttergamedev.com/using-your-dev-skills-for-testing)**
