TDD for Tic Tac Toe using Java (part 2)



In previous episodes:

Cheers everyone. These days I am working on a project which has lots of scientific stuff under the hood - finds best route between point A and point B depending on various circumstances. So what you need to do is to evaluate lots of possibilities, build graph, measure coefficients...And it is something certainly which is horrible thing for manual testing by definition.

And still we had some kind of disagreement about what approach we should take to ensure quality on this project with project tech lead - her suggestion was to do heavy manual testing cause "TDD is not applicable on such kind of projects", mine was that unit-testing may be the only way to create basis for a good QA on a project, and TDD is probably the only way to guarantee we do unit-testing properly.

 So, just for lulz, I decided to play with idea a little bit and chose some kind of similar application to work with - Tic Tac Toe game. One of the possible solutions to make computer play against you would be evaluate possible moves, build graph, measure coefficients (sounds kind of familiar, ah?).

In this and following part we're going to add some interaction with user and allow two players to play with each other. If you haven't read first part you can read it here.

So, first of all, I think it would be easiest to add simple method for printing current state of our board. Naturally it is something we can do using toString() and then rout what ever it returns into System.out. Just checking that toString() output is not null will happily and wrongly pass - because toString() has default dummy implementation. So, to make test fail we need already to think how we want to see out board printed. For me, it should be something as easy as these examples:

[ . . . ]   [ . . . ]   [ . x . ]   [ . x . ]
[ . . . ]   [ . o . ]   [ . o . ]   [ . o . ]
[ . . . ]   [ . . . ]   [ . . . ]   [ o x . ]

To achieve this, we probably again would need to employ data-driven test approach. The real question would be is to if we should try all combinations (a huge number of possible combinations, to be honest), or again try some subset. Using XP  mantra "do the simplest thing that may possibly work" I am going to choose a subset of 6 possible states of the board, representing an imaginary game:

[ . . . ] [ . . . ] [ . o . ] [ . o . ] [ . o o ] [ x o o ]
[ . . . ] [ . x . ] [ . x . ] [ . x . ] [ . x . ] [ . x . ]
[ . . . ] [ . . . ] [ . . . ] [ . . x ] [ . . x ] [ . . x ]


I didn't add much rocket science, boundaries, equivalence classes or any other testing techniques, but I think that this set of states should be reasonably good enough for me to create appropriate code. To test empty board would be the simplest - I will just add separate method for that.


Of course it is going to fail - no implementation is there yet. And trying to be TDD purist, we will add dummy method, which just prints three "." in square brackets three times in row. Method I came up with will be pretty much easy to update to be doing right things, but believe me or not - I was just trying to do dummy loops and avoid code duplication to make bloody testEmptyBoardToString() pass!


My implementation was wrong, though - I forgotten about line break, but that was easy to fix. Being lazy again I will add other tests as a data driven tests with moves and expected table states as test input. Here what I came up with:


To achieve necessary functionality I had to slightly update toString() method and toString() to the PlayBoard, and it worked like a charm (omitting code for first update as it really straight forward, right?).

This revealed, though, that my test was not correct - playBoard is being cleaned up each time, so I have to fix this as well. As a result I just moved this test into another class, fixing another typo on the way.


So now we're able to print our board, and we need not much to make our game playable - just add ability to input from hotseat players. We will achieve that using System.in functionality. It will also allow as to easily mock this in test - as we're going to work with streams, which are pretty easy to simulate. However, we need to think about what abstraction is actually is going to handle this interaction between the user and board, so we need to think about our application architecture beforehand.

I decided not to reinvent the wheel and to use famous (or notorious - depends on your stand) MVC architecture pattern (more can be read in this post). The only kind of complication here would be that I use command line as a means of showing and interacting, but we still can consider this as a kind of "view". I change my project structure accordingly, moving all classes I had into model package, and will consider view package.

As for now I have really vague understanding of how I am going to do things in view, I probably will start from Interface, where I will identify, what I want it to do. At the very basic case, I want my view to:
  • show current state of the board
  • read user input
Fair enough, so I will create an BoardView  interface (struggling not to call this IBoardView after several month of working with C#, which will be covered in other post). My BoardView has only two methods for now, which are showBoard and addUserInputListener. Even though my current implementation will involve only command line, this interface should give me enough flexibility to move into UI if I need. This will add a little bit of awkwardness though, as I clearly don't want to use ActionListener for command line application, so I will create my brand new Listener, called UserActionListener (which is also an interface). At this point I clearly violated vanilla TDD rule of not writing code without test, but I will put my thoughts on that in the summary part of the series.

Anyway, I am ready to add test for my (not existent yet) ConsoleBoardView implementation - ConsoleBoardViewTest, and first thing I am going to test going to be a showBoard, which I am going to use to show initial and updated state of the board. As was decided before, I am going to route output into a test stream, so I will need a setter for that (but default stream should be a System.out), so I also would have to test a constructor for that. Code for test and implementation is below.



That being done I will test AddUserInputListener, for which I will also need setter. Code is a little bit complicated below, as I don't have any concrete UserActionListener yet, so I decided to use cool lambda stuff instead of anonymous class here for that.



I think this particular post is getting to big already, so I will stop here. It is so much fun, but I probably can do it forever, so I just have to stop somewhere. To be honest, TDD is more pleasant that I had anticipated, even though I already used this before for some time.

See you next time and stay tuned - after experiment is over I am going to summarize my idea about TDD and it's applicability to various tasks.

Comments

Popular posts from this blog

Test automation framework architecture. Part 2 - Layered architecture

Test automation framework architecture. Preface.

Test automation framework architecture. Part 1 - No architecture