Test Driven Development and Observer Practice


In this lab, you are going to work on extending a set of (reasonably simple) classes that keep track of accounts and the networth of sets of accounts. The portion of the design that we are currently worried about contains two classes: Account and Networth. Since we are using TDD, they already have some JUnit tests: AccountTest and NetworthTest. Finally, we have a test suite called AllTests that runs the tests on both classes.

Step 1:

Get all of this code into a (new) project in Eclipse and don't go on until all seven tests in AllTests have run. At this point, you should see that I've done a pretty crummy job of thinking of all of the tests that would be required for a real system. This is just a toy lab to make a few critical points!

Step 2:

Just to practice writing tests (that are more thorough than mine!), you have to add the ability to delete accounts from networth instances. The first test you should write just needs to put an account into a networth and then delete it. Develop the test, write enough code to make it compile and fail, then write the code to make it pass. How will you verify that it was deleted?

Step 3:

Clearly, there are some exception cases that deleting accounts needs to deal with. In particular, what if someone tries to delete an account that isn't there. Write a test that makes sure the appropriate exception gets thrown, watch it fail, and then write the code to make it pass. Note: I have found a simpler way to write tests for exceptions than the one I gave you in the notes, look through the tests that came with this lab to find it!

Step 4:

There are also some border cases that you should test for: deleting the most recently added account (when there are more than one), deleting an account in the middle of a series you've added, and deleting all of the accounts (when there are more than one). For this step, write a test that puts 5 account into a networth and deletes the most recent one. If it fails, write the code to make it pass.

Step 5:

The next border case I want you to write should put 6 accounts into a networth and delete them all. Remember: test code should be as neat as production code. Since you are going to do similar things in multiple tests (like put a number of accounts into a networth), use the Extract Method refactoring to make your existing code into a method so that you can call it from both tests. (If that is jibberish, put up your hand and I'll explain). If necessary, write the code to make the test pass.

Step 6:

While there are more border cases you might want to worry about, let's move on to the next problem. Notice that Networth is Observable. There are classes in the user interface that display its value and they want to be updated when the value changes. Unfortunately, they are only notified (via the notifyObservers() method call) when a new account is added. They are complaining that they do not get updated when an account is deleted. First, you need a test that can verify that the observers of a Networth got updated. To build that, create the following inner (private) class within your NetworthTest class:

	private class MockObserver implements Observer
{
boolean updated = false;

public void update(Observable arg0, Object arg1) {
updated = true;
}
}

Now, you can create an instance of MockObserver, add it as an observer to an instance of Networth, do whatever you need to Networth (in this case, delete an account) and verify that variable "updated" in your MockObserver is true. Once you have written that test, if it fails, write the code to make it pass (if it passes, you were more thorough in your development than I expected - sorry for doubting you!)
As in step 5, do NOT duplicate the code; extract what you need from the addAccount method and call it from the deleteAccount method.

Step 7:

The observers of Networth are still complaining because they don't get notified of a change in value when the balances of the accounts in a networth change. For example, when a deposit is made in an account, the value of any networth instances that hold that account should be updated and their observers should be notified.

First, write a test that verifies the defect: Create a Networth and add an Account instance to it. Create a MockObserver to watch the Networth instance. Make a deposit to the Account class. Test to be sure that the MockObserver got updated. This test will fail because Networths don't do anything on deposits to their accounts.

In order to fix this, we are going to have to make Networth be an observer of each of its accounts. This will require a sequence of changes:

Now your test should pass. (If you've been thorough, you should be able to write tests to verify that Networth (and its observers) get updated when anything it done to an account (deposit or withdraw) or a networth (add or delete account)).