Difference between revisions of "Roguelike Iterative Test-Driven Development in Java, Part 3"

From RogueBasin
Jump to navigation Jump to search
 
(8 intermediate revisions by one other user not shown)
Line 15: Line 15:


==Design==
==Design==
If you've never done Test-Driven Development before this is going to sound rather lame. In order to make the most simple portions of our program we'll need to over-engineer them in order to make them testable. Specifically we need to abstract the basic services (screen output and keyboard input) behind interfaces that abstract the implementation of the service from the consumption of that service.
The main menu and game modes are part of our ''view layer'', or the user-facing interface. Our view layer has no functional requirements, only aesthetic requirements: does it look good, is it intuitive, is all pertinent information displayed, etc. Aesthetic requirements are tested in the acceptance testing phase by the customer who, in this case, will be me. We'll use an abstract base class to provide common functionality.


Luckily for us [[libjcsi]] takes care of the screen output abstraction. It does not provide a test implementation but we don't really need one. Screen output is our ''view layer'', like the HTML rendering portion of a web application or the presentation layer of a thin-client application. View layers typically have many aesthetic requirements and few, if any functional requirements. We don't unit test aesthetic requirements; that's what acceptance testing is for. And our particular view layer will have no functional requirements, so it will not be unit tested.
Screen output has already been abstracted for us by [[libjcsi]]. Unfortunately so has keyboard input, and it's part of the same abstraction layer. We need to provide an abstraction of user input that is not directly tied to the keyboard so we can support key bindings and macros later on. We also need to expose the raw input for special situations, like naming a character or selecting items by letter. We won't be doing unit tests that are driven by input (because we're not unit testing the game modes) so we won't need a testing layer for this.


Some concrete implementation is required for the other portions of the system, and I'd rather not have console windows popping open and going crazy while the unit tests run. For that reason we'll develop a ''stub implementation'' of libjcsi's output interface. Stub implementations are just what they say they are: a concrete implementation of an interface in which most or all methods are stubs. This effectively makes every operation a no-op.
Surprisingly enough this leaves us with nothing to unit test.


libjcsi also abstracts keyboard input for us. The problem is there's no way to provide mock input. We need an additional layer of abstraction if we want to support key mappings and macros in the future, so instead of creating a test implementation for keyboard input we'll go ahead and create the input abstraction interface, a test implementation and an intermediate implementation that translates key presses into input actions.
===Classes===
* '''com.qbradq.durok.lib.ConsoleSystem''' - Class wrapping our screen output and keyboard input services and includes our input abstraction
* '''com.qbradq.durok.game.GameMode''' - Interface for anything that enters an input consumption loop
** '''com.qbradq.durok.game.MainMenuMode''' - The main menu
** '''com.qbradq.durok.game.MainGameMode''' - The main game interface


Menu and game modes are not service providers, rather they are service consumers. We won't need multiple levels of abstraction to test them, however a single layer of abstraction (an interface) allows easier expansion and more flexible design. And a base implementation (base class) helps code reuse.
===Screen Mockups===
Main Menu
<pre>
+------------------------------------------------------------------------------+
|                                                                              |
|                              The Halls of Durok                            |
|                                                                              |
|                        by Norman B. Lancaster (QBRADQ)                     |
|                                                                              |
|                    n) Start a New Game                                      |
|                    r) Resume your Last Game                                  |
|                    q) Quit                                                  |
|                                                                              |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro  |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro  |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro  |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro  |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro  |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro  |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro  |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro  |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro  |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro  |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro  |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro  |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro  |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro  |
|                                                                              |
+------------------------------------------------------------------------------+
</pre>


So here's what our class diagram looks like:
Main Game UI
* '''net.slashie.libjcsi.ConsoleSystemInterface''' - Console system interface provided by libjcsi
<pre>
** '''com.qbradq.durok.lib.ConsoleSystem''' - Base class wrapping our screen output and keyboard input services
+------------------------------------------------------------------------------+
*** '''com.qbradq.durok.impl.test.TestConsoleSystem''' - Stub implementation of console services for testing
|#################################                                            |
*** '''com.qbradq.durok.impl.JCursesConsoleSystem''' - Implementation based on JCurses
|#################################                                            |
*** '''com.qbradq.durok.impl.WSwingConsoleSystem''' - Implementation based on WSwing
|#################################                                            |
*** '''com.qbradq.durok.impl.ConsoleSystemFactory''' - Factory provider for ConsoleSystem implementations
|#################################                                            |
* '''com.qbradq.durok.lib.InputProviderInterface''' - Interface for the input service provider
|#################################                                            |
** '''com.qbradq.durok.impl.test.TestInputProvider''' - Implementation specializing in providing pre-defined test input streams
|#################################                                            |
** '''com.qbradq.durok.impl.InteractiveInputProvider''' - Implementation that converts interactive user input into input streams
|#################################                                            |
* '''com.qbradq.durok.game.GameModeInterface''' - Interface for anything that enters an input consumption loop
|#################################                                            |
** '''com.qbradq.durok.game.GameModeBase''' - Base implementation of a game mode providing sane default behavior
|#################################                                            |
*** '''com.qbradq.durok.game.MainMenuMode''' - The main menu
|#################################                                            |
*** '''com.qbradq.durok.game.GameMode''' - The main game interface
|#################################                                            |
|#################################                                            |
|#################################                                            |
|#################################                                            |
|#################################                                            |
|#################################                                            |
|#################################                                            |
|Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Lo|
|Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Lo|
|Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Lo|
|Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Lo|
|Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Lo|
|Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Lo|
|Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log L |
+------------------------------------------------------------------------------+
</pre>


==Tests==
==Tests==
None. This felt very awkward to me, but after some thought I realized that even though we don't have a unit test yet we are still ''driving'' the development cycle by considering our ''tests'' first.
We will still need to do acceptance testing at the end of the development cycle.
==Implementation==
I started by creating a [http://code.google.com/p/durok/ Google Code project page] for the project. Then I started Eclipse, installed the Google plugins, imported the project page and created a new Java project. I then brought in my dependencies and fiddled with things until the libjcsi examples worked.
==See Also==
[[Roguelike Iterative Test-Driven Development in Java]]
[[Category:Developing]]

Latest revision as of 00:45, 13 October 2012

In this part we finally get to write some code! This part corresponds to HWR15 Part 3. In a traditional waterfall development pattern this is the easiest part to implement because it's the most basic. It's also the time during which you have the most opportunity to make a design mistake that will cause you grief later on.

Requirements

  1. Screen Output
  2. Keyboard Input
  3. Main Menu
  4. Game Mode
    1. Message Display
    2. Test Character Display
    3. Movement Keys

Personally I like to start with a main menu. It seems like when I design a game starting with the game mode it's harder to add a menu later.

Design

The main menu and game modes are part of our view layer, or the user-facing interface. Our view layer has no functional requirements, only aesthetic requirements: does it look good, is it intuitive, is all pertinent information displayed, etc. Aesthetic requirements are tested in the acceptance testing phase by the customer who, in this case, will be me. We'll use an abstract base class to provide common functionality.

Screen output has already been abstracted for us by libjcsi. Unfortunately so has keyboard input, and it's part of the same abstraction layer. We need to provide an abstraction of user input that is not directly tied to the keyboard so we can support key bindings and macros later on. We also need to expose the raw input for special situations, like naming a character or selecting items by letter. We won't be doing unit tests that are driven by input (because we're not unit testing the game modes) so we won't need a testing layer for this.

Surprisingly enough this leaves us with nothing to unit test.

Classes

  • com.qbradq.durok.lib.ConsoleSystem - Class wrapping our screen output and keyboard input services and includes our input abstraction
  • com.qbradq.durok.game.GameMode - Interface for anything that enters an input consumption loop
    • com.qbradq.durok.game.MainMenuMode - The main menu
    • com.qbradq.durok.game.MainGameMode - The main game interface

Screen Mockups

Main Menu

+------------------------------------------------------------------------------+
|                                                                              |
|                               The Halls of Durok                             |
|                                                                              |
|                         by Norman B. Lancaster (QBRADQ)                      |
|                                                                              |
|                    n) Start a New Game                                       |
|                    r) Resume your Last Game                                  |
|                    q) Quit                                                   |
|                                                                              |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro   |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro   |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro   |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro   |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro   |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro   |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro   |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro   |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro   |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro   |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro   |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro   |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro   |
|    Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro Intro   |
|                                                                              |
+------------------------------------------------------------------------------+

Main Game UI

+------------------------------------------------------------------------------+
|#################################                                             |
|#################################                                             |
|#################################                                             |
|#################################                                             |
|#################################                                             |
|#################################                                             |
|#################################                                             |
|#################################                                             |
|#################################                                             |
|#################################                                             |
|#################################                                             |
|#################################                                             |
|#################################                                             |
|#################################                                             |
|#################################                                             |
|#################################                                             |
|#################################                                             |
|Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Lo|
|Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Lo|
|Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Lo|
|Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Lo|
|Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Lo|
|Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Lo|
|Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log Log L |
+------------------------------------------------------------------------------+

Tests

None. This felt very awkward to me, but after some thought I realized that even though we don't have a unit test yet we are still driving the development cycle by considering our tests first.

We will still need to do acceptance testing at the end of the development cycle.

Implementation

I started by creating a Google Code project page for the project. Then I started Eclipse, installed the Google plugins, imported the project page and created a new Java project. I then brought in my dependencies and fiddled with things until the libjcsi examples worked.

See Also

Roguelike Iterative Test-Driven Development in Java