testing and tdd for java developerswakaleo.com/public_resources/tdd-lab-exercises.pdfthis workbook...

38
Testing and TDD for Java Developers Lab Exercises

Upload: others

Post on 25-Apr-2020

12 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Testing and TDD for Java Developers

Lab Exercises

! !

Page 2: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

! !

Page 3: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Table of Contents

Introduction! 4

Lab 1. Setting up your environment! 5

Lab 2 - Getting Started with TDD ! 17

Lab 3 - Working with fixtures! 22

Lab 4 - An introduction to Hamcrest asserts ! 23

Lab 5 - Handling Exceptions ! 26

Lab 6 - Parameterized tests ! 27

Lab 7 - Working with Stubs in Mockito ! 28

Lab 8 - Working with Servlets! 29

Lab 9 - Working with JWebUnit! 30

Lab 10 - Working with Selenium! 31

Lab 11 - Your first Easyb story! 32

Lab 12 - Fixing bugs with easyb! 34

Lab 13 - Fixtures in easyb! 36

Lab 14 - Web tests in Easyb! 37

! !

Page 4: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

IntroductionThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of a sequence of lab exercises which will introduce the concepts of Test Driven Development for Java developers. These lab exercises should be completed in sequence in the presence of an instructor. The instructor will distribute the software and code necessary to complete these exercises. If, at any time during the lab exercise, you encounter problems with your software installation or if you do not understand any of the instructions, please ask your instructor for help.

This lab manual and training materials assume that you have been supplied with the necessary software prerequisites. Your instructor will distribute installation media or provide instructions for downloading this material from the Internet. The supporting software required for this course are:

• Java Development Kit version 1.6.0_15 (JDK)

• Mercurial 1.3.1

• Eclipse 3.5

• Apache Maven 2.2.0

The labs assume that a recent version of Java (JDK 1.5.0 or higher), Mercurial and Maven have been installed on the workstations. Mercurial is not strictly necessary, but you will need it if you want to consult the lab solutions or starting points.

! !

Page 5: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab 1. Setting up your environmentGoal

The aim of this lab exercise is to set up your workstation with the tools required for this workshop. After completing this exercise, you should be able to set up and configure these tools on other workstations.

Agenda

• Install Eclipse

• Install the m2eclipse

• Install the EclEmma plugin

• Install the Infinitest plugin

• Import the tweeter project into your Eclipse workspace

• Verify that the tweeter application builds and runs correctly

Lab Exercises

In this exercise, you are going to download (if necessary) and install Eclipse onto your workstation, and install the plugins required for the following lab exercises. If you are doing this exercise in a lab environment, your instructor will have provided you with a CD containing the appropriate software packages.

If your lab environment has pre-configured lab machines (your instructor will tell you if this is the case), you can go directly to Step 6.

Step 1. Download Eclipse

Download the Eclipse IDE for Java EE developers from the Eclipse web site (the recommended version is 3.5.SR1), or copy it from the provided lab CD.

! !

Page 6: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Unzip the Eclipse installation on your hard disk. The following list shows the recommended installation paths for each platform:

Operation System Installation DirectoryWindows C:\Program Files\eclipse

Mac OS /Applications/eclipse

Unix/Linux /usr/local/eclipse

If you are working on Windows Vista you may need administrator priviledges to install files into "C:\Program Files". If this is not possible, you can always install Eclipse in another directory, such as "C:\Tools\eclipse".

! !

Page 7: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Step 2: Adding the -vm Argument for m2eclipse

This step is only relevant for students who are using a Windows operating system. If you are running on a Mac OSX or Linux environment, you can skip this step.

To use m2eclipse, you need to run Eclipse with the JDK. However, when running Eclipse on Windows, Eclipse may run by default with the JRE (Java Runtime Edition) version of Java. If this happens, you might get an error.

To get around this, you need to ensure that Eclipse is running under a JDK. You can do this by using the -vm option to provide a path to your Java JDK. To do this:

1. Create a shortcut on the desktop (drag the eclipse icon onto the desktop while pressing ALT):

2. Modify the shortcut properties, right-click on the shortcut and select Properties.

3. Click on the Shortcut tab in the "Shortcut to eclipse.exe Properties" dialog.

4. Add the text: -vm "C:\Program Files\Java\jdk1.6.0_16\bin" to the Target. After adding this argument the text in Target should contain the following text (including the quotes):

! !

Page 8: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

5. You may also need to define a JDK in the Eclipse Preferences. Open the "Window / Preferences.../ Java / Installed JREs" screen and add a reference to your JDK if it is not already present. You should also tick the JDK installation to make it the default JDK.

! !

Page 9: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Step 3: Install the m2Eclipse plugin

The labs exercises in this course use Maven. You donʼt need to know Maven to do the exercises (what you need to know will be explained as we go), but you do need to install the m2eclipse plugin in Eclipse so that Eclipse can work with the Maven projects.

Start up Eclipse and go to the “Help...Install New Software” menu. Then click on “Add Site” and add the m2eclipse update URL: http://m2eclipse.sonatype.org/update-dev

! !

Page 10: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Choose the Maven Integration item. You donʼt need the optional items for these lab exercises. Then click through the screens to install the components and restart Eclipse

We will use the embedded installation of Maven for these labs, which normally works well. However, some users have reported some issues with this version. If problems occur, you can also use an external instance of Maven instead.

In a lab environment, this will have been installed into the C:\tools\maven-2.2.1 directory on your workstations. To use this installation instead, open the “Eclipse->Preferences” menu, and go to the “Maven/Installations” section. Then use the “Add...” button to add an external installation of maven.

! !

Page 11: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Step 4: Install the ECLEmma plugin

Code coverage is a useful indicator that helps see if your tests are indeed exercising your code comprehensively. Using TDD generally results in very high test coverage metrics. We will be able to visualise this using the ECLEmma plugin.

Go to the “Help...Install New Software” menu. Then click on “Add Site” , and use the EclEmma update site URL of http://update.eclemma.org/. Step through the steps and restart Eclipse.

Step 5: Install Infinitest

Infinitest enables you to run your unit tests whenever you save your code, which leads to very tight feedback loops.

Go to the “Help...Install New Software” menu. Then click on “Add Site” , and use the Infinitest update site URL of http://update.improvingworks.com. Step through the steps and restart Eclipse.

! !

Page 12: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Step 6: Installing the lab solutions project

During this workshop, we will be working on a sample application called Tweeter, which is a simple Twitter clone. A basic project structure is provided for you to get you started, in the form of a Mercurial repository. The recommended starting points of each lab, as well as sample lab solutions, are tagged in this repository.

The lab source code, including the Mercurial repository, are in the lab-solutions directory on the workshop CD. Copy the tweeter sub-directory onto your local hard drive. While you can place this directory anywhere you like, the recommended directory target for the purpose of these labs is as follows:

Operation System Installation DirectoryWindows C:\Projects\tweeter

Mac OS ~/Projects/tweeter

Unix/Linux ~/projects/tweeter

Step 7: Import the tweeter project into Eclipse

First, import the tweeter project into Eclipse. Select “Files->Import” from the Eclipse menu and choose the “Maven Projects” import type.

! !

Page 13: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

In the next screen, select the tweeter project directory: make sure all of the sub-projects are ticked:

Once this is done, Eclipse will import the Tweeter project into your workspace:

! !

Page 14: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Now right-click on each project, open the Properties window, and deactivate the “Resolve dependencies for Workspace projects”.

! !

Page 15: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Step 7: Verify that the tweeter application builds and runs correctly

Now we will check that the application builds and runs correctly. You can do this from the command line, or directly from within Eclipse. From within Eclipse, go to the tweeter parent project and use the “Run As -> Maven install” command from the contextual menu to do a complete build and test cycle of the application.You can also do this from the command line in the tweeter root directory:

C:\Projects\tweeter> mvn install

This should build and test the application. To see the application running, you can run it using Jetty either from Eclipse or on the command line. From the command line, go to the tweeter-web directory and run the Maven Jetty plugin as follows:

C:\Projects\tweeter\tweeter-web> mvn jetty:run

Alternatively, you can run the same command in Eclipse, by creating a special run configuration to invoke the “jetty:run” command, as shown below.

! !

Page 16: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

The application will now be running on port 8080:

Now check that the tests run correctly. To run all of the unit tests, go to the Tweeter module and select “Run As...Maven Test”.

You can also run unit tests directly from Eclipse for the tweeter-core and tweeter-services modules, using “Run As...JUnit Test”. The tweeter-web module contains web tests, so for this to work in Eclipse, you must first start up the jetty web server either from the command line or from within Eclipse, as shown above.

Note: If you have started Jetty in Eclipse or from the command line, you can also run all of the tests, including the web tests in the tweeter-web module, directly from Eclipse using . Otherwise, the web tests

Now that you have seen the application build and run successfully, we will concentrate on the tweeter-core module for the remainder of this lab.

! !

Page 17: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab 2 - Getting Started with TDDGoal

The aim of this lab exercise is to add a new feature to the Tweeter application. You will use the Tweeter project provided on the workshop CD as a starting point. The Tweeter project is a multi-module Maven project with the following architecture

Module Descriptiontweeter Parent/aggregator module for the whole project

tweeter-core Core domain layer

tweeter-services A service layer, interface between the core layer and the UI

tweeter-web The tweeter web interface

You will start working on an early version of the Tweeter application, which implements an initial, simple prototype. Throughout the labs, you will get the chance to add new features using a variety of unit testing tools, always using a TDD/BDD approach.

For this exercise, we will be concentrating on the tweeter-core module. This module contains the domain model of the Tweeter application. In the Tweeter object, tweeterrs (aka users) publish tweeters (messages) on the Tweeter web site.

Agenda

• Add a new feature to tweeter-core using a TDD approach

Using Mercurial

Sample solutions and initial starting points for each lab are stored in the Mercurial repository on your CD. To return to the initial starting point for this lab, run the following command from the lab-solutions root directory:

$ hg update lab-02-start

To obtain the sample solution for this lab, do the following:

$ hg update lab-02-solution

You can also do the same thing from Windows Explorer using the TortoiseHG contextual menu, as shown below:

! !

Page 18: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

You can select the lab or lab solution you want in the ʻUpdate toʼ drop-down list:

! !

Page 19: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab Exercises

Step 1: Ensure that the tweeter-core tests run successfully

Open tweeter-core in Eclipse and run the unit tests. You can do this by choosing “Run As...JUnit Tests” in the contextual menu. You should see 7 tests, all of which pass.

Step 2: Check the code coverage

Now use the EclEmma plugin to check the code coverage metrics of the tweeter-core module. You can do this by running “Coverage As -> JUnit Tests”. You should see that the domain layer code has 100% test coverage.

Step 3: A new feature - tags

We are going to start off by adding a simple new feature to the tweeter-core module. Our user requirement is the following.

User Requirement

"As a tweeter userI want to be able to place tags on a tweeterSo that I can more easily find tweeters about similar topics"

This requirement has filtered down to the tweeter-core layer, where we now have to add the required features to the domain model. (At this stage, for the purposes of this exercise, we wonʼt be worrying about acceptance tests just yet).

Working in pairs, come up with a list of tests that can be used to validate this new feature. Some of the questions you may want to elucidate might include:

! !

Page 20: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

• Can a tweeter have no tags at all?

• What happens if I add the same tag twice?

and so forth. A sample list of tests is shown here:

• You can add a single tag to a tweeter;

• You can add multiple tags to a tweeter;

• You can remove a tag from a tweeter;

• When you add the same tag twice, it is only stored once;

Step 4: Write the test

Now it is time to write your first test. First, create a test class called TagTest.java in the src/test/java/com/wakaleo/training/tweeter/domain directory and add your first test case. The initial structure might look like this:

package com.wakaleo.training.tweeter.domain;

import static org.junit.Assert.assertEquals;import org.junit.Test;

public class TagTest {

@Test public void iCanAddATagToATweeter() { }}

Now implement this test using the JUnit techniques you have seen in the previous modules. Since this is your first test, weʼll give you a clue: One possible implementation might be as follows:

package com.wakaleo.training.tweeter.domain;

import static org.junit.Assert.assertEquals;import static org.junit.Assert.assertTrue;

import java.util.List;

import org.junit.Test;

public class TagTest {

@Test public void iCanAddATagToATweet() {

! !

Page 21: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Tweet tweet = new Tweet("Hi there"); tweet.addTag("java"); List<String> tags = tweet.getTags(); assertTrue(tags.contains("java")); assertEquals(1, tags.size()); }}

When you save the completed test, you will need to add empty methods in the Tweet class corresponding to the methods you have just dreamt up in the test case. Then run the JUnit tests to ensure that this new test is executed, and fails.

Step 5: Write the code

Now implement the code and rerun the tests.

Step 6: Refactor

Always check your code after you have written it to see if there are ways you might improve it and make it more readable and maintainable. This will of course depend on your code, and there may be nothing to do at certain stages (particularly at the start)

Step 7: Run your tests through EclEmma to verify the test coverage. It should remain at 100%.

Step 8: Now repeat this process for the other tests in your test list.

! !

Page 22: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab 3 - Working with fixturesGoal

There is a lot of unnecessary repetition in the current tweeter-core unit tests. The aim of this lab exercise is to refactor the tests we did in the previous exercise, using JUnit 4 fixtures.

Agenda

• Refactor your existing tests using fixtures

Lab Exercises

Step 1: Refactor the TweeterTest class

Refactor the TweeterTest class using the @Before fixture. You should be able to refactor the tests so that each test contains only a single assert, as shown in the code sample below:

public class TweeterTest { ...

@Before public void setup() { ... }

@Test public void earlyTweetsShouldComeLast() { assertThat(tweet2, lessThan(tweet1)); } ...}Make sure you check that the unit tests run successfully after each change.

Step 2: Refactor the TweeterUserTest class

See if you can refactor the TweeterUserTest class in the same way.

Step 2: Refactor the TagTest class

See if you can refactor the TagTest class in the same way.

! !

Page 23: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab 4 - An introduction to Hamcrest assertsWe will be using Hamcrest asserts throughout the rest of this course. To get you started, here are a few basic exercises.

Step 1) Create a new test class called HamcrestTest, as follows:

import static org.junit.Assert.assertEquals;

import static org.hamcrest.Matchers.*;

import static org.hamcrest.MatcherAssert.assertThat;

import org.junit.Test;

public class HamcrestTest {

@Test public void testAssertThatIs(){

String color = "red";

}

}

Complete the testAssertThatIs() method with a Hamcrest assert that checks that color is equal to “red”. Hint: use assertThat() method with the is() matcher.

Change the value of the color variable. What is the error message produced?

Step 2) Add and complete the following method using the is matcher. What advantages does it have over the equivalent assertEquals() expression?

@Test

public void testAssertThatIsForDoubles(){

double expectedResult = 100.0;

double calculatedResult = 10.0 * 10.0;

assertThat(...)

}

Step 3) Add and complete the following method using the isIn matcher. @Test

public void testAssertThatIsIn(){

String[] colors = new String[] {"red","green","blue"};

String color = "blue";

assertThat(..);

}

! !

Page 24: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Step 4) Add and complete the following method using the not and isIn matcher. @Test

public void testAssertThatIsNot(){

String[] colors = new String[] {"red","green","blue"};

String color = "yellow";

assertThat(...);

}

Step 5) Add and complete the following method using the isOneOf matcher. The test should verify that color is either “red”, “green” or “blue. @Test

public void testAssertThatIsOneOfRedGreenBlue(){

String color = "blue";

assertThat(...);

}

Step 6) Add and complete the following method using the notNullValue matcher. The test should verify that color is not null @Test

public void testAssertThatIsNotNull(){

String color = "blue";

assertThat(...);

}

Step 7) Add and complete the following method using the hasItem matcher. The test should verify that the colors list contains the value “green”. @Test

public void testAssertThatHasItem(){

List<String> colors = new ArrayList<String>();

colors.add("red");

colors.add("green");

colors.add("blue");

assertThat(...);

}

Step 8) Add and complete the following method using the not, hasItem and lessThan matchers. The test should verify that the ages list contains no value that is less than 18. @Test

public void testAssertThatNoneLessThan18(){

List<Integer> ages = new ArrayList<Integer>();

ages.add(20);

! !

Page 25: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

ages.add(30);

ages.add(40);

assertThat(...);

}

! !

Page 26: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab 5 - Handling ExceptionsUsers have requested a blacklist of tags that should not be added to a babble's tags. This black list contains precisely the words "bother", "fudge" and "ni" - the users are unaware of any other words that might need to go in this list. Our user requirement is the following.

User Requirement

"As a system adminI do not want users to be able to add the tags 'bother', 'fudge' or 'ni'Because these tags might offend other users."

We have decided to implement this requirement directly in the domain core layer, so we will be modifying the Tweet class.

Step 1) Add a test to check that you cannot add any of these words, e.g.

@Test public void blacklistedTagsCannotBeAdded() {...}

Step 2) Implement a minimal implementation to make this requirement work.

The users have also indicated that they would like feedback on when a blacklisted tag has been refused:

User Requirement

"As a userI want to know when a tag that I enter has been blacklistedSo that I can understand why the tag is not appearing in the list when I add it."

Step 2) Ensure that the addTag() method now throws an exception if blacklisted words are thrown.

We will do this by raising an exception when a blacklisted word is entered (use the "expected" parameter in the @Test annotation). Write a test that checks that a BlacklistedTagException exception is thrown when a user enters a blacklisted word. Use Eclipse to create the BlacklistedTagException class, which is thrown if a black-listed tag is added to a babble. You will need to refactor many of your tests to get this to compile once again.

Add a test called "addingABlacklistedWordShouldRaiseAnException()".

Then refactor the blacklistedWordsCannotBeAddedToATagList() test so that it tests both that no blacklisted works were added, but also that the correct words were still correctly added to the list.

Once you have refactored the tests, and have a compiling test that fails, refactor the code to handle the exceptions correctly.

! !

Page 27: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab 6 - Parameterized testsThe business owner wants to add a premium service with a dynamic pricing system. Users will be able to send coloured tweets for an extra fee, using the following monthly pricing scale:

• 1-100 tweets: 10c / babble

• 101-500 tweets: 8c / babble

• 501+ tweets: 5c / babble

We will implement this by adding a PremiumTweetsService class. This class will have a method along the following lines:

double calculateFeesDue(int messageCount)

Use a parameterized test to create a test for this class to be run against a significant selection of data. Remember, write the PremiumTweetsServiceTest test case first. Once the tests are written, use the Eclipse "quick-fix" feature to create the PremiumTweetsService class and the calculateFeesDue() method. When you save the PremiumTweetsService class with the empty calculateFeesDue() method, the Infinitest tests should fail.

Now implement the method.

Hint (Optional): One way to do this is to write a class that knows how to calculate prices for a particular fee rates bracket (say, called "TweetFeeRange"). If you use this approach, write the tests for this class first! Do this by writing the calculateFeesDue() method the way you would like to be able to use the TweetFeeRange class, then write some tests to ensure that the TweetFeeRange class does what you expect. Then implement the TweetFeeRange class.

Discuss how you might import test data from an Excel spreadsheet.

! !

Page 28: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab 7 - Working with Stubs in MockitoThe next feature the user has requested is the ability for new users to be able to sign up and create a new Tweeter account.

User Requirement

"As a new user I want to create an account So that I can post my own messages"

When we discussed this requirement with the user, she gave us a few more details:

• Users cannot create an account with a name that already exists• Users have to provide a password• The password canʼt be empty• The password canʼt be identical to the username

To implement this feature, we are going to create a SignupService interface, a SignupServiceImpl class, and a SignupServiceTest test class. The signup service will require access to a user directory. We already have a UserDirectory interface, implemented by the working InMemoryUserDirectoryImpl class. In a real application, the user directory would access an external resource (database, LDAP server, etc), so for our signup tests we are going to assume it works correctly and stub it out.

Step 1: Create the SignupService interface, the SignupServiceImpl class, and the SignupServiceTest test class. The SignupService interface should look something like this:

public interface SignupService {

void setUserDirectory(UserDirectory userDirectory);

TweeterUser signup(String username, String password) throws UserExistsException;

}

Step 2: In the SignupServiceTest class, implement a test to ensure that users cannot create an account with an existing username. Use Mockito to create a UserDirectory stub that returns a valid TweeterUser object when you call the findUserByUsername() method.

Step 3: Now implement a test called userSignupShouldAddAUserToTheDirectory() to ensure that the signup service invokes the addUser() method on the UserDirectory once (and only once).

Step 4: Implement another test called userSignupShouldReturnNewUser() to check that the method returns the new user object.

Step 5 (Optional): Write tests for, and implement, the other business rules indicated above.

! !

Page 29: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab 8 - Working with ServletsFor this lab, we will continue with the signup feature. We will create a new SignupServlet class, along with a SignupServletTest test class. The SignupServlet will use the SignupService class, and will take two parameters: “username” and “password”. If the signup is successful, the servlet should place the new TweeterUser in the session and redirect to the application home page (“/tweeter-web/home”), otherwise it should place an error message in the request attributes and forward to the “/signin.jsp” page.

Step 1: Write a SignupServletTest test class and a SignupServlet class.

Step 2: Write a unit test called successfullSignupShouldReturnToHome(), to check that the servlet redirects to the home page if the signup succeeded. Use Mockito and the Spring Servlet API mock classes to mock out the service class and the request and response objects.

Step 3: Write a unit test called signupShouldLogUserOn(), to check that the servlet stores the new user object in the session.

Step 4: Write a unit test called signupWithExistingUserShouldReturnToSignupJSP(), to check that the servlet forwards to the “signup.jsp” page if the user already exists (hint - you will need to use Mockito to do interaction testing here).

Step 5: Write a unit test called signupWithExistingUserShouldStoreErrorMessage() to check that the servlet stores an appropriate error message in the request scope.

Step 6: Write a unit test called signupWithExistingUserShouldStoreErrorMessage() to check that the servlet stores an appropriate error message in the request scope.

Step 7: Write a unit test called signupWithExistingUserShouldNotLogon() to check that the servlet should not store any user objects in the session if the user account already exists.

Step 8 (Optional): Write tests for, and implement, the other business rules indicated in the previous section.

! !

Page 30: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab 9 - Working with JWebUnitFor this lab, we will implement the user interface for the signup feature. You can use the existing project structure, and just add a TestSignupStory class that extends the AbstractBaseStory class in the webtests/jwebunit directory. Have a look at the other tests here to get a feel for how they work.

Step 1: Write a ʻhappy dayʼ scenario in a test called testUserSignupShouldAddNewUser(). A possible scenario would be:

1. Start off at the home page

2. Click on a link with the text ʻSign up now!ʼ

3. Check that the page title now displays ʻTweeter / Create an Accountʼ

4. Type ʻjackoʼ and ʻsecretʼ in the username and password fields respectively.

5. Submit the form

6. Ensure that the screen now displays the welcome message ʻHi jacko!ʼ

To run this test, you need to start up Jetty on the command line. Open a DOS window in the tweeter-web directory and type “mvn jetty:run”. Then return to your IDE.

Once this test is written (and failing), modify the index.jsp and create a new file called signup.jsp to implement this form. You will also need to update the web.xml form to include the signup servlet and configure the signupService bean in your application-config.xml file (the configuration will be very similar to the loginService bean.

Step 2: Now try some error-handling. Write scenarios to handle:

• The user enters an existing username (hint: the user ʻscottʼ exists by default in the in-memory user directory) and an error message is displayed.

• The user enters an empty username

• The user enters an empty password

• The user enters an empty username and an empty password

! !

Page 31: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab 10 - Working with SeleniumSelenium tests are similar in purpose to JWebUnit tests, though they work somewhat differently - Selenium tests run though a browser, whereas JWebUnit tests simulate the browser. In this lab, we will implement the same tests as in the previous lab, but using Selenium.

Step 1: Set up your Maven project to use Selenium (or update your workspace to the lab starting point).

Step 2: Write Selenium tests for the scenarios described in the previous lab.

! !

Page 32: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab 11 - Your first Easyb storyBefore starting, install the Eclipse Groovy plugin and the Eclipse easyb plugin, using following update sites respectively:

• http://dist.codehaus.org/groovy/distributions/greclipse/snapshot/e3.5/

• http://easyb.googlecode.com/svn/trunk/eclipse-plugins/org.easyb.eclipse.updatesite/

In this lab, we will be using Easyb to write some BDD-style unit tests. To get familiar with Easyb, we will be rewriting some of the existing unit tests for the tweeter-core module using easyb. Create a file called tagStory.groovy in the src/test/easyb folder, in a package called com.wakaleo.training.tweeter.domain.

This test will contain our first Easyb story:

package com.wakaleo.training.tweeter.domain;

scenario "I can add a tag", { given "a new tweet" when "I add a tag to a tweet" then "The tag list should contain 1 element" and "the tag list should contain the tag I added"}

For now, this is just an empty specification. However, you can still run it in Eclipse and via Maven. In Eclipse, click on “Run” or choose “Run As -> Behavior” in the contextual menu. You should see a message in the Console indicating “1 behavior ran (including 1 pending behavior) with no failures”.

! !

Page 33: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

You can also run easyb through Maven to generate an HTML report. Click on the project folder and choose “Run As -> Maven Test”. This will generate an HTML report in the target/easyb directory:

Now we will implement the test. A possible implementation would be as shown here:

package com.wakaleo.training.tweeter.domain;

scenario "I can add a tag", { given "a new tweet", { tweet = new Tweet("Hi there") } when "I add a tag to a tweet", { tweet.addTag("Easy As!") } then "The tag list should contain 1 element", { tweet.tags.size.shouldBe 1 } and "the tag list should contain the tag I added", { tweet.tags.shouldHave "Easy As!" }}

Rerun the tests in Eclipse and from Maven, and look at the new HTML report output. You should now get something like the following:

! !

Page 34: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab 12 - Fixing bugs with easybIn this lab we will be working with the blacklist features in the tweeter-core module. A user has noticed that if the blacklisted words are entered in upper case, they are not rejected.

The easyb stories are in the src/test/easyb directory. Add a flle called ʻblacklistedTags.storyʼ to this directory.

scenario "Blacklisted words in uppercase should also be blocked", { given "a tweet with a set of authorised tags" when "I try to add a blacklisted word in uppercase" then "a BlacklistedTagException is thrown" and "the tag list should remain unchanged"}

You can run this scenario from within Eclipse (using “Run” or “Run as -> Behavior”). You should obtain the following output:

Running blacklisted tags story (BlacklistedTags.story)Scenarios run: 1, Failures: 0, Pending: 1, Time elapsed: 1.201 sec

1 behavior ran (including 1 pending behavior) with no failures

You should get a report like this in the target/easyb directory (note the ʻpendingʼ story):

Now implement this test, to confirm that the problem exists. A possible implementation (using the Hamcrest asserts) is shown here:

import com.wakaleo.training.tweeter.domain.Tweet;import static org.hamcrest.MatcherAssert.assertThat;import static org.hamcrest.Matchers.*;

scenario "Blacklisted words in uppercase should also be blocked", { given "a tweet with a set of authorised tags", { tweet = new Tweet("Hi there").addTags(["java","web"]) } when "I try to add a blacklisted word in uppercase", { tweet.addTag("FUDGE") } then "the tag list should remain unchanged", { assertThat tweet.tags, is(["java", "web"]) }}

! !

Page 35: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

When you run this test, the test should fail:

Running blacklisted tags story (BlacklistedTags.story)

FAILURE Scenarios run: 1, Failures: 1, Pending: 0, Time elapsed: 1.194 sec

scenario "Blacklisted words in uppercase should also be blocked"

step "a BlacklistedTagException is thrown" -- expected exception of type [class com.wakaleo.training.tweeter.domain.BlacklistedTagException] was not thrown

scenario "Blacklisted words in uppercase should also be blocked"

step "the tag list should remain unchanged" --

Expected: is <[java, web]>

got: <[FUDGE, java, web]>

1 behavior ran with 1 failure

Now fix the issue and ensure that the easyb story runs correctly.

Now add another scenario testing what happens when a group of tags containing a blacklisted word is added. It should add all of the tags except for the blacklisted word, and throw an exception. A possible story might be the following.

scenario "Adding a group of tags containing blacklisted words" given "a tweet with a set of authorised tags" when "I try to add a collection of tags containing blacklisted words" then "a BlacklistedTagException is thrown" and "only the valid tags should have been added"}

Implement this story and ensure that it works. A possible implementation (which involves some minor refactoring of the Tweet class) would be the following:

scenario "Adding a group of tags containing blacklisted words", { given "a tweet with a set of authorised tags", { tweet = new Tweet("Hi there") tweet.addTags(["java","web"]) } when "I try to add a collection of tags containing blacklisted words", { addBlacklistedTagList = { tweet.addTags(["Fudge","groovy","ni"]) } } then "a BlacklistedTagException is thrown", { ensureThrows(BlacklistedTagException.class) { addBlacklistedTagList() } } and "only the valid tags should have been added", { assertThat tweet.tags, is(["groovy", "java", "web"]) }}

! !

Page 36: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab 13 - Fixtures in easybIn this part of the lab, we will be refactoring the previous story using the shared behaviour fixtures. Regroup the shared behaviour into ʻbefore_eachʼ clauses.

! !

Page 37: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Lab 14 - Web tests in EasybIn this part of the lab, we will be writing a web test in Easyb. We will be using JWebUnit to write some simple user acceptance-style web tests. For this lab, the tweeter-web project has been configured to run the easyb tests during the integration test phase. The web application will be deployed to an embedded Jetty server just before the integration test phase.

Step 1: Create a new easyb story called UserSignup.story, in the src/test/easyb directory:

scenario "User signup should add a new user", { when "I click on the sign up button on the home page" and "I enter a new username and password" then "the application should log me on as the new user and show a welcome message"}

Run this story to ensure that it works - you should get something like the following:

Running signup user story (SignupUser.story)

Scenarios run: 1, Failures: 0, Pending: 1, Time elapsed: 1.015 sec

1 behavior ran (including 1 pending behavior) with no failures

Now implement the test. You can use the “before” keyword to initialise a jWebUnit tester object:

import net.sourceforge.jwebunit.junit.WebTester

before "initialize a web test client", { given "we have a web test client", { tester = new WebTester() tester.setBaseUrl("http://localhost:8080/tweeter-web") }}Next, implement the class itself. A sample implementation is shown here:

scenario "User signup should add a new user", { when "I click on the sign up button on the home page", { tester.beginAt("/home") tester.clickLinkWithText("Sign up now!") } and "I enter a new username and password", { tester.setTextField("username", "jane") tester.setTextField("password", "tiger") tester.submit() } then "the application should log me on and show a welcome message", { tester.assertTextPresent("Hi jane!") }}

! !

Page 38: Testing and TDD for Java Developerswakaleo.com/public_resources/tdd-lab-exercises.pdfThis workbook is designed to accompany Wakaleoʼs TDD for Java training course. It consists of

Step 2: Write a user acceptance test for user logins:

scenario "A user logs on with a username and password", { when "I have a valid username and password" and "I go to the home page" and "I enter my username and password" then "the application should log me on and show a welcome message"}

Step 3 (Optional): Write a user acceptance test for posting messages.

! !