Made a few updates to the Java Maven project with Cucumber Acceptance Test demo. The Gherkin format Acceptance Criteria is now included in the post for easy copy and past and additional details about the StackSteps class are included.
In this tutorial, we will walk through creating a basic JUnit test class in Eclipse just to make sure that our environment is set up properly. We will use Test Driven Development (AKA Test First Development) to ensure that we are writing just enough code to pass our test, and not writing our test to pass our code. This process can be used to test and confirm that you are getting the correct output from your code.
We will create a single Junit test method, review the various dependancies, and confirm that there are 0 errors in your code. Using this process can help to save time & energy that may otherwise be expended on reviewing and re-writing code that doesn’t work.
First we will create a new Java project, then we will create a JUnit test that describes the functionality that we expect from our code. In addition, we will use code generation tools in Eclipse (templates) to create the class and it’s contained methods. Lets get started!
On the File menu, select: New > Java Project
Name the Project EchoApp, and click Finish
Expand the EchoApp project
Right click on the src folder
On the popup menu select: New > Class
Name your new class TestEcho, and accept all defaults by clicking Finish
- Type the following code into your project (Notice the red squigglies beneath some of your annotations)
- Hover over the @Test annotation with the red squiggly beneath it
- Select “Add JUnit 4 library to the build path” to make the red squiggly disappear
- Hover over the assertEquals annotation
- Select Add static import ‘org.junit.Assert*’ to
add the assert import and make the red squiggly go away
- Hover your mouse over Echo and select Create class ‘Echo’ to create the Echo class and make the red squiggly disappear
- Accept default settings and click Finish
An empty public class has now been created:
- Click on the TestEcho.java tab to return to your class (Notice that now, a red squiggly is beneath echo)
- Hover over echo, and you will see ‘1 quick fix available:’
- Select Create method ‘echo(String)’ in type ‘Echo’ to create a Public Static String method
- Right click on the @Test annotation, select Run as > 1 Junit Test
- Eclipse will prompt you to save your TestEcho and your new Echo class
- Click OK to save the files
- Uh oh! It looks like your test has failed L…
- Click the Echo.java tab (Notice that the default method template has set the return value to null)
- Change null to expected
- Click the TestEcho.java tab to return to your test class
- Right click on the @Test annotation
- Select Run as > 1Junit test
- A dialogue box will appear that asks if you want to save your resources. Select OK
Your test will now run and pass with 0 errors and 0 failures:
In this demo, we created a single Junit test method, revied the dependancies, and ran the test to confirm that there were 0 errors in our code. .
Java Development Kit
See the post on installing Jenkins to convert this simple Java Project to a Maven Project and build and test with Jenkins.
The Test-Driven Development (TDD) Processes follows a pattern known as the TDD Rhythm which dictates the order in which elements of the solution should be created / edited.
Before we can successfully implement TDD a few key agile constructs must exist. Most importantly we must have Tasks derived from a User Stories (or requirements) that define the details of required system feature. These defined details would include the Acceptance Criteria for feature described in the User Story. We would then use the task details and acceptance criteria to define our tests.
The TDD Rhythm
1. Write a Failing Test
The first step in the TDD Rhythm is to Write a Failing Test. Using the Task Details, we write a test that exercises the functionality defined by the Users Story and expects that the value that is returned is the same as the value that is expected based on the Acceptance Criteria defined in the Task Details.
2. Run the Failing Test
Run the test to see it fail. This is an interesting step as depending on your application architecture may require some minimal project structure be created and project references made for your Failing Tests to even compile before they can run and fail. For example, if you are storing all of your Business Logic in a Class Library Project called BusinessRules that compiles as a Windows .dll and your Tests are centrally stored in a Test Project then your Class Library Project will have to Exist and the Namespace, Class and Method will have to exist before your Test Project will compile and the Tests will run and fail. Fortunately, Visual Studio includes code generation tools that will create the Classes and Methods as long as the Class Library Project Exists and at least one class with a Namespace statement exists. The Failing Test generated by Visual Studio will throw a NotImplementedException which will obviously cause the method to fail.
3. Write just enough code to pass the test
This can be a difficult concept to get your mind around for especially when the common simple TDD example code is used. For example, take a method simply returns a Boolean value to illustrate simple TDD method creation if we start with a test that runs the required Boolean method and expect the method to return a true the code to pass the test would simply be return True;
Figure A. Test Method to test GetBool Method
Figure B. Minimal code needed to pass the test
With this example it may seem like a waste of time to write this minimal code to pass the test as it is clear that the test needs a returned value of true in order to pass so where is the value is writing this useless passing test? Without writing the test that tests the “unhappy path” through our method (aka the test that expects a return value of false) it is hard to see the value is a method that simply returns true with no additional implementation logic. An eager developer may want to just skip to writing implementation logic without wasting time on the simplest code step of the TDD Rhythm but follow the pattern young Jedi. As seen in a slightly more complex method that returns a formatted string, understanding how the output should be formatted in order to pass the test can potentially be much more difficult than just returning a Boolean value of true.
string expected = “Welcome Back Antoine! Your last visit to the site was 02/01/2016.”;
string actual = WebSite.BizRules.WelcomeBack(user);
string message = “We should get “ + expected;
Assert.AreEqual(expected, actual, message);
Figure C. Test Method to test WelcomeBack Method
string WelcomeBack(object user)
“Welcome Back Antoine! Your last visit to the site was 02 / 01 / 2016.”;
Figure D. Minimal code needed to pass the test
In this example the Literal String returned including the user name Antoine and the last visit date would obviously need to be updated for each user and on each daily visit but the formatting and welcome statement may also be important and could possibly come from a configuration file somewhere. The point is that the minimal code required to pass the test in this case acts as documentation for the method including formatting requirements for returned values. On the next refactoring pass we would update the code in the method to include the code necessary to extract the user name from the user object passed to the method and retrieve the date of their last visit from the membership database and return it in the expected format. The String Literal is our formatting template as we create the implementation code we know what the expected result format looks like.
4. Run the Passing Test
At this point our method has just enough code to pass the test but does not necessarily meet the business requirement nor does it allow us to perform the task described by the user story. This will become obvious as more tests are developed to tests the “unhappy path” or as varying return values are expected by other tests. But at this point we understand what must be done for the test to pass and can keep that in mind as we refactor the code to make it meet the business requirement or for optimization purposes.
5. Refactor the Code
Depending on “minimal” code we wrote to pass the test our first refactoring pass may be to add required functionality or if the required functionality already exist we may be refactoring for Maintainability, Scalability or Performance Optimization. In any event as we refactor the code for whatever reason we can do so with the confidence that any changes that we make have tests in place to ensure that we have not made a change that would break existing functionality already passing tests. If you make a change and all of the sudden tests that were passing stop passing you know you have a problem. The tests can also be used for Gated Check-ins that require that any changes a developer makes to code must pass existing tests before it can be checked into Source Control allow bugs to be identified before they make into our build and potentially out to customers.
The Expanded TDD Rhythm
6. Run All Tests
Once we have refactored our code to include desired functionality or optimize for maintenance, scalability or performance we need to run all tests to ensure that our changes did not break the method that we were working on but also for any methods that depend on this method or its results. This is a necessary step to avoid failed check-ins on Source Control or Continuous Integration (CI) Servers where Gated Check-ins are used. With gated check-ins your check-in cannot break the automated build and all tests must pass or your check-in will be rejected and your code not allowed into source control until the issues are resolved.
As changes are required we can continue to repeat this process of writing failing tests, coding, passing tests and refactoring until our code for the features we are adding for our updates is “perfect”