Chapter 9. Iterating and Testing: The Software is Still for the Customer

image with no caption

It’s time to show the customer how much you really care. Nagging bosses? Worried clients? Stakeholders that keep asking, “Will it be done on time?” No amount of well-designed code will please your customers; you’ve got to show them something working. And now that you’ve got a solid OO programming toolkit, it’s time to learn how you can prove to the customer that your software works. In this chapter, we learn about two ways to dive deeper into your software’s functionality, and give the customer that warm feeling in their chest that makes them say, Yes, you’re definitely the right developer for this job!

Your toolbox is filling up

We’ve learned quite a bit so far, and our toolbox of analysis and design tools is getting pretty full. We even added some OO programming techniques in the last chapter:

image with no caption
image with no caption
image with no caption
image with no caption
image with no caption

But you’re still writing your software for the CUSTOMER!

All the tools and techniques you’ve been learning are terrific... but none of them matter if you don’t use them to produce great software that makes your customer happy.

And most of the time, your customer won’t care about all the OO principles and diagrams you create. They just want the software to work the way that it’s supposed to.

image with no caption

Joe: Yeah, maybe we shouldn’t have spent all this time on so many diagrams, and all this architecture stuff. We’ve got nothing to show Gary except a bunch of ovals with things like “Play Game” written inside them.

Frank: Come on guys, we’ve got a lot more done than that. It’s going to be simple to finish up the Board class, because we’ve already got a start on writing a lot of that functionality.

Jill: Well, sure, but that’s the only class we’ve written any code for. How are we supposed to show that to Gary?

Joe: Well, I guess we could write the Unit class pretty easily, since we did that class diagram. So it wouldn’t take a lot more time to write the code for that class.

Frank: Exactly. And, really, we know how to write all of these classes. We can just take each class, or even an entire package, and apply all those OO principles and analysis and design techniques to each chunk of functionality.

Jill: But we’ve got to work on functionality now. We don’t have time for a bunch more big-picture analysis and design.

Frank: But that’s just the thing, Jill: we don’t need to change what we’re doing, we just need to iterate deeper.

Joe: Iterate deeper? What does that mean?

Frank: It just means we keep doing analysis and design, but now on each individual part of Gary’s game system framework.

Jill: And as we build up the application, we’ll have lots of pieces working that we can show to Gary, right?

Joe: And we get to use all these tools we’ve got to make sure the software is well-designed, too, right?

Frank: Exactly. But first, we’ve got a choice to make...

You write great software iteratively.

Work on the big picture, and then iterate over pieces of the app until it’s complete.

Iterating deeper: two basic choices

When it comes to developing software, there is more than one way to iterate into specific parts of your application. You’ve got to take on smaller pieces of functionality, but there are two basic approaches to figuring out which small pieces to work on—and even what a “small piece” means in terms of your application.

You can choose to focus on specific features of the application. This approach is all about taking one piece of functionality that the customer wants, and working on that functionality until it’s complete.

Both approaches to iterating are driven by good requirements.

Feature Ahead

image with no caption

Feature driven development

...is when you pick a specific feature in your app, and plan, analyze, and develop that feature to completion.

Because requirements come from the customer, both approaches focus on delivering what the customer wants.

You can also choose to focus on specific flows through the application. This approach takes a complete path through the application, with a clear start and end, and implements that path in your code.

Note

image with no caption

Use case driven development

...is when you pick a scenario through a use case, and write code to support that complete scenario through the use case.

You’ll often see the terms “flow” and “scenario” used interchangeably.

Feature driven development

When you’re using feature driven development, you work on a single feature at a time, and then iterate, knocking off features one at a time until you’ve finished up the functionality of an application.

image with no caption
image with no caption

Use case driven development

With use case driven development, you work on completing a single scenario through a use case. Then you take another scenario and work through it, until all of the use case’s scenarios are complete. Then you iterate to the next use case, until all your use cases are working.

image with no caption
image with no caption

Two approaches to development

There’s just one basic way to write code, isn’t there? Well, there are actually a ton of different ways to go about iterating deeper and finishing up parts of your application. Most of these different approaches fall into the two basic categories we’ve been looking at, though. So how do you decide which to use?

What’s the difference between feature driven and use case driven development?

Feature driven development is more granular

Use case driven development is more “big picture”

Works well when you have a lot of different features that don’t interconnect a whole lot.

Works well when your app has lots of processes and scenarios rather than individual pieces of functionality.

Allows you to show the customer working code faster.

Allows you to show the customer bigger pieces of functionality at each stage of development.

Is very functionality-driven. You’re not going to forget about any features using feature driven development.

Is very user-centric. You’ll code for all the different ways a user can use your system with use case driven development.

Works particularly well on transactional systems, where the system is largely defined by lengthy, complicated processes.

Works particularly well on systems with lots of disconnected pieces of functionality.

Let’s use feature driven development

Since Gary’s losing patience, let’s go with feature driven development. We can take just a single feature and work it through to completion, and it shouldn’t take as much time as it would to write the code to support an entire use case.

Anytime you’ve got a customer impatient to see results, you should consider feature driven development, and starting with a feature you’ve already done some work on.

image with no caption

Brain Power

If you decided to go with use case driven development, what would you start working on first?

Analysis of a feature

Once you’ve decided on a feature to start with, you’ve got to do some more analysis. Let’s start with what we had written down on the feature list:

3. The framework supports multiple types of troops or units that are game-specific

Note

Here’s what we’ve got so far... but this is still a pretty generic description of what we need to code.

We also have the start of a class diagram, from Chapter 7:

image with no caption

It looks like we’ve got everything we need to start coding, right? To help us make sure we haven’t forgotten anything, let’s go back to using some textual analysis.

We don’t have a use case to analyze, but we can revisit the vision statement for Gary’s games, and see if we’re covering everything that Gary wanted his units to do.

Compare the class diagram for Unit with this vision statement. Are there things missing from our class diagram?

What else might Gary expect to see when you say, “I’m done with writing code for the units in your framework?”

Fleshing out the Unit class

In our class diagram, all we’ve really figured out is how to represent the properties of a unit. But in Gary’s vision statement, he’s expecting his game system framework to support a lot more than just those game-specific properties.

Note

This makes sense, because the key feature we were focusing on in Chapter 7 was not the entire Unit class, but just game-specific properties of a Unit.

Here are the things we came up with that Gary is expecting units in his framework to do:

  1. Each unit should have properties, and game designers can add new properties to unit types in their own games.

    Note

    Our class diagram is focused on this particular aspect of the Unit class right now.

  2. Units have to be able to move from one tile on a board to another.

    Note

    You should have some ideas about how to handle this from our work on a related key feature back in Chapter 7.

  3. Units can be grouped together into armies.

    Note

    These new features are all pulled straight from Gary’s vision statement.

image with no caption

Showing off the Unit class

image with no caption

We worked on supporting game-specific units, and how to store the properties of a Unit, back in Chapter 7. But Gary wants more than a class diagram before he’s convinced you’re getting any work done.

image with no caption
image with no caption

Your customers want to see something that makes sense to them

Your customers are used to seeing computer programs run on a computer. All those diagrams and lists may help you get on the same page with them in terms of requirements and what you’re supposed to build, but you’re going to need more than that before they think you’ve built anything useful.

You need to come up with some test scenarios that you can show to your customer, which will prove that your code works, and that it behaves like your customer expects it to.

Writing test scenarios

Note

Be careful... this “scenario” isn’t the same as the “scenario” we’ve been talking about in a use case scenario.

Test cases don’t have to be very complex; they just provide a way to show your customer that the functionality in your classes is working correctly.

For the properties of a unit, we can start out with a simple test scenario that creates a new Unit, and adds a property to the unit. We could just show our customer a running program that displays output like this:

image with no caption

Scenario #2: Changing property values

We decided to test setting, and then changing, the value of a property. If the hitPoints property is set, for example, and then set again, getting the value of hitPoints should return the most recent value for that property:

image with no caption

Scenario #3: Getting non-existent property values

For our third scenario, we decided to test what would happen when you tried to retrieve the value of a property that had never been set. Error conditions like this crop up all the time, and we don’t want our program crashing every time a game designer makes a small typo or mistake in their code. Here’s what we did to test this:

image with no caption

You should test your software for every possible usage you can think of. Be creative!

Don’t forget to test for incorrect usage of the software, too. You’ll catch errors early, and make your customers very happy.

image with no caption
image with no caption

Joe: What do you mean? We figured out that all properties in a Unit have a name and a value. So we decided to use a Map to store them all.

Frank: And game designers can add any new properties they want by just creating new property names, and sticking name/value pairs in the Map with the setProperty() method.

Jill: Right. But then, we also added a type property, since all units will have a type. And that’s something common for all units...

Joe: Sure. See, we did do the commonality analysis right.

Jill: ...but we also now know that units can be assembled into groups, like armies or fleets or whatever. So what happens if we have two units of the same type in the same group... how can we tell the difference between them?

Frank: You think we need some sort of ID, don’t you?

Jill: Yeah, maybe. Or at least a name... but even then, you can’t really prevent duplication with a name property, can you?

Joe: OK, but that still doesn’t mean we need to change our design. We can just add the ID property into our property Map. So we’ve got a nice, uniform way to access all those properties, using the getProperty() method.

Frank: He’s right, Jill. And since we encapsulate away the details about property names into the properties Map, we could even change from using an ID to a name, or something totally different, and code using the Unit class wouldn’t have to change much... you’d just need to use the new property name in getProperty(). That’s a pretty slick design!

Jill: But what about commonality? If ID is really common to all types of Unit, shouldn’t it be moved out of the Map, sort of like we did with the type property?

Joe: Whoa... encapsulation or commonality. That’s tough... it seems like we can’t do one without screwing up the other.

image with no caption
image with no caption
image with no caption
image with no caption

Solution #1: Emphasizing Commonality

image with no caption
image with no caption

In this solution, all game designers can directly access the id, name, and weapons properties, instead of having to use getProperty() and work through the more generic properties Map.

The emphasis is on keeping the common properties of a Unit outside of the properties Map, and leaving properties that vary inside the properties Map.

image with no caption

Design decisions are always a tradeoff

Sam chose to emphasize the things that are common across all types of Units. But there are some negatives to Sam’s design, too:

Note

We’re repeating ourselves

Now there are two different ways to access properties: through the getId(), getName(), and property-specific methods, and the getProperty() method. Two ways to access properties is almost certainly going to mean duplicate code somewhere.

image with no caption

Maintenance is a problem

Note

When you see the potential for duplicate code, you’ll almost always find maintenance and flexibility issues, as well.

Now you’ve got property names, like id and name, hard-coded into the Unit class. If a game designer doesn’t want to use those, or wants to change them, it’s going to be a real hassle, and require changes to the Unit class. This is usually where encapsulation would help, and that leads us to Randy’s design choice...

image with no caption
image with no caption

Solution #2: Emphasizing Encapsulation

image with no caption
image with no caption

This solution focuses on encapsulating all the properties for a Unit into the properties Map, and providing a standard interface—the getProperty() method—for accessing all properties. Even properties that apply to all units, like type and id, are accessed through the properties Map in this solution.

The emphasis is on encapsulation, and a flexible design. Even if the names of common properties change, the Unit class can stay the same, since no property names are hardcoded into the class itself.

image with no caption

Tradeoffs with this decision, too...

Randy’s solution is more resistant to changes, and uses a lot more encapsulation, but there are tradeoffs with this design, as well. Here are a few of the downsides to Randy’s design:

We’re ignoring commonality Randy encapsulated all of the properties into the properties Map, but now there’s nothing to indicate that type, name, id, and weapons are intended to be properties common to all Unit types.

image with no caption

Lots of work at runtime getProperty() returns an Object, and you’re going to have to cast that into the right value type for each different property, all at runtime. That’s a lot of casting, and a lot of extra work that your code has to do at runtime, even for the properties that are common to all Unit types.

image with no caption

Brain Power

Which developer’s solution do you think is best? Are there times where you think one solution might be the best choice, and other times where the other might work better?

Let’s go with the commonality-focused solution

For Gary’s game system framework, let’s use Sam’s solution, which pulls the common properties of a Unit out into their own properties and methods, and leaves unit-specific properties in a separate Map.

image with no caption
image with no caption
image with no caption

Match your tests to your design

We’ve got test scenarios we want to show Gary, and a design for the Unit class. The last thing we need to do before coding is to make sure our design for Unit will allow us to code a solution that passes all the tests.

image with no caption

Let’s write the Unit class

It’s been two chapters in coming, but we’re finally ready to write the code for the Unit class. Here’s how we did it:

image with no caption
image with no caption
image with no caption

Test cases dissected...

We’ve talked a lot about test cases, but so far, you haven’t seen how to actually write one. Let’s examine a test case up close, and see exactly what makes up a good test.

image with no caption
  1. Each test case should have an ID and a name.

    Note

    Try not to refer to tests as test1, test2, etc. Use descriptive names whenever possible.

    The names of your test cases should describe what is being tested. Test names with nothing but a number at the end aren’t nearly as helpful as names like testProperty() or testCreation(). You should also use a numeric ID, so you can easily list your tests cases out (something we’ll do on the next page).

  2. Each test case should have one specific thing that it tests.

    Note

    One piece of functionality may involve one method, two methods, or even multiple classes... but to start with, focus on very simple pieces of functionality, one at a time.

    Each of your test cases should be atomic: each should test only one piece of functionality at time. This allows you to isolate exactly what piece of functionality might not be working in your application.

  3. Each test case should have an input you supply.

    Note

    If you’re setting hitPoints to 15, then “15” becomes the input you supply to your test case.

    You’re going to give the test case a value, or a set of values, that it uses as the test data. This data is usually then used to execute some specific piece of functionality or behavior.

  4. Each test case should have an output that you expect.

    Note

    This is what you want the program to output. So if you set type to “infantry”, and then call getType(), your expected output is “infantry”.

    Given your input, what should the program, class, or method output? You’ll compare the actual output of the program with your expected output, and if they match, then you’ve got a successful test, and your software works.

  5. Most test cases have a starting state.

    Note

    There’s not much starting state for the Unit class. We do need to create a new Unit, but that’s about it.

    Do you need to open a database connection, or create a certain object, or set some values before running your test? If so, that’s all part of the starting state of the test case, and needs to be handled before you run the actual test.

image with no caption
image with no caption

And now for some bonus credit...

We added three new test cases to our table, to handle the three properties common to all units that aren’t tested in UnitTester. You should be able to write three additional test methods based on this table. Did you figure these out on your own?

image with no caption
image with no caption

Prove yourself to the customer

With a Unit class and a set of test cases, you’re ready to show Gary some working code, and prove that you’re on the right track to building his game system framework just the way he wants it. Let’s show him the test class running:

image with no caption
image with no caption
image with no caption

Let’s change the programming contract for the game system

When you’re writing software, you’re also creating a contract between that software and the people that use it. The contract details how the software will work when certain actions are taken—like requesting a non-existent property on a unit.

If the customer wants an action to result in different behavior, then you’re changing the contract. So if Gary’s framework should throw an exception when a non-existent property is queried, that’s fine; it just means that the contract between game designers and the framework has changed.

When you program by contract, you and your software’s users are agreeing that your software will behave in a certain way.

Note

Want to know more about what this means? Turn the page to find out more...

We’ve been programming by contract so far

You probably didn’t notice, but we’ve been doing something called programming by contract in the Unit class so far. In the Unit class, if someone asks for a property that doesn’t exist, we’ve just returned null. We’ve been doing the same thing in getWeapons(); if the weapons list isn’t initialized, we just return null there, too:

image with no caption
image with no caption

This is the contract for Unit

The Unit class assumes that people using it are competent programmers, and that they can handle null return values. So our contract states something like this:

image with no caption

Programming by contract is really all about trust

When you return null, you’re trusting programmers to be able to deal with null return values. Programmers are basically saying that they’ve coded things well enough that they won’t ask for non-existent properties or weapons, so their code just doesn’t worry about getting null values back from the Unit class:

image with no caption

And we can always change the contract if we need to...

Back in Note, we were asked to stop returning null, and throw an exception instead. This really isn’t a big change to the contract; it just means that now game designers are going to have big problems if they ask for non-existent properties or weapons.

image with no caption
image with no caption
image with no caption

But if you don’t trust your users...

But what happens if you don’t think your code will be used correctly? Or if you think that certain actions are such a bad idea that you don’t want to let users deal with them in their own way? In these cases, you may want to consider defensive programming.

Suppose you were really worried that game designers using the Unit class, and asking for non-existent properties, were getting null values and not handling them properly. You might rewrite the getProperty() method like this:

image with no caption
image with no caption

I’m sure you’re great code and all, but I just don’t trust you. I could send you null, and you could totally blow up. So let’s just be safe, and I’ll send you a checked exception that you’ll have to catch, to make sure you don’t get a null value back and do something stupid with it.

image with no caption

-or if they don’t trust you...

Of course, when programmers use your code, they might not trust you either... they can program defensively as well. What if they don’t believe that you’ll only return non-null values from getProperty()? Then they’re going to protect their code, and use defensive programming, as well:

image with no caption
image with no caption
image with no caption

When you are programming by contract, you’re working with client code to agree on how you’ll handle problem situations.

When you’re programming defensively, you’re making sure the client gets a “safe” response, no matter what the client wants to have happen.

image with no caption

Moving units

image with no caption

We’ve finally finished up unit properties, and can move on to the next item on our list:

  1. Each unit should have properties, and game designers can add new properties to unit types in their own games.

  2. Units have to be able to move from one tile on a board to another.

    Note

    We’re on to dealing with movement, now.

  3. Units can be grouped together into armies.

Haven’t we been here before?

This should sound pretty familiar... we already dealt with movement in Chapter 7:

image with no caption

Break your apps up into smaller chunks of functionality

We’ve been talking a lot about iterating deeper into your application, and at each stage, doing more analysis and design. So you’re taking each problem, and then breaking it up (either into use cases or features), and then solving a part of the problem, over and over. This is what we’ve been doing in this chapter: taking a single feature, and working on that feature until it’s complete.

image with no caption

But you can still break things up further...

But once you choose a single feature or use case, you can usually break that feature up into even smaller pieces of behavior. For example, a unit has properties and we have to deal with unit movement. And we also need to support groupings of units. So each of these individual pieces of behavior has to be dealt with.

image with no caption

Just like when you broke your app up and began to iterate, you’ll have to do more analysis and design at each step. Always make sure your earlier decisions make sense, and change or rework those decisions if they don’t.

Your decisions can iterate down, too

Lots of times you’ll find that decisions you made earlier save you work down the line. In Gary’s system, we decided that game designers would deal with movement on their own. So now that we’re talking about how to handle unit movement, we can take that decision we made earlier, and apply it here. Since it still seems sensible—there’s no reason to change that decision—we can have game designers worry about handling unit movement, and move on to the next piece of behavior.

image with no caption

Note

Just add a note to your docs for the game designers that movement is up to them.

image with no caption
image with no caption
image with no caption

And now for the test cases:

image with no caption

Get online

You should be able to write the code for UnitTester using the test case table shown here. If you want to see how your code compares with ours, visit http://www.headfirstlabs.com, click on Head First OOA&D, and look for “UnitGroup Test Cases.”

    Reset