Archive

Posts Tagged ‘junit’

And Now By Popular Demand: jWebUnit

December 13, 2009 1 comment

In the land of TDD things start pretty raw (plain vanilla JUnit) and slowly work their way out. While there are all sorts of areas to test (databases, web services, servlets, EJBs, POJOs and others) the fact is you not only have to be conscious of your testing, but you have to also decide how much testing to do. During the implementation of web applications what you find out is that your tests can do at least one of two things:

  • flow testing
  • integration testing

The thing is you don’t want any one test to do both. You should have one test for flow (unit) testing and another for integration testing.

Are there other test types? Of course (you just have to read JUnit Recipes to figure that out). Being the lazy guy that I am I really only perform functional and integration tests when I write my own code and rely on the customer to decide how far down the rabbit hole they want to go. How can you help them decide how much testing to do? Ask a simple question: how much would they lose if something went wrong? This is a question of risk tolerance. Software responsible for life and death should be tested until the cows come home. Software that is responsible for making/saving money should be tested to the customers limit of legal liability; after that test to what they can afford, but at least now you have a baseline.

But I digress. It must be the squirrels.

There are a lot of frameworks for web app testing and I have arbitrarily chosen jWebUnit as the winner because I am boring and stuck in the past thoughtful and forward-looking and I have just not had time to look at all of the available choices some of which are:

[I remember looking at FitNesse and Selenium and even bought a book on FitNesse, but never got so far as to actually implement anything with it. My loss. It looks really good.]

There are so many things to say about what you should be thinking and how you should be thinking in regards to your tests. My recommendation is that you should use your use cases to guide your integration testing (you are writing use cases, aren’t you?) and just get to work. If you want philosophical guidance (and I am never short of that) you should read as much as you can about TDD and testing at all of the various levels. For example:

[Just as an aside: I love Spring. TDD is so easy with Spring. You test every level of your application (db, POJOs, messaging, web pages) as if they were all POJOs…because they are. If you can use Spring to develop your apps, web or otherwise, please use it. I can’t imagine developing an application without it.]

Enough mayonnaise. What are we doing? How are we doing it? Why did we do it that way?

What (are we doing?)

The Use Case:
Actor: A Visitor (not like from V, but they could use this too)

Scenario 1:
1a: The Visitor, wanting to register for a special Hidden Clause prize, is on the registration page. The registration form has the following fields:

  • Name (no more than 30 characters, no numbers)
  • Password (hidden)

If they user does not enter information in either or both fields display the form again and ask them to enter valid information.

1b: When the visitor has submitted the registration page they will be asked to confirm their information.

1c. When they confirm that the information we have is correct a Success page will reassure them that they will be receiving their HC prize any day now.

Scenario 2:
2a. As above in 1a.

2b. When the visitor has submitted the registration page they will be asked to confirm their information. If the information is incorrect open the registration form again with their current information.

2c. As above in 1c.

Scenario 3:
3a. As above in 1a.

3b. As above in 1b.

3c. The registration process failed due to a system failure. Apologize to the user and ask them to try again later or call in for their prize.

The tests:

  • Scenario 1
    • Good scenario
      1. Check title for login page
      2. Fill in the two fields with valid information
      3. Submit login page
      4. Check title for confirmation
      5. Confirm that the content we entered was accepted
      6. Submit confirmation page
      7. Check title for success page
      8. Confirm success message
    • Error scenario 1 – required fields empty
      1. Go to login page
      2. Submit login page without filling in the fields
      3. Check login page for error message
    • Error scenario 2 – required content is invalid
      1. Go to login page
      2. Fill in the two fields with invalid information
      3. Submit login information
      4. Check login page for error message
    • Error scenario 3 – unknown error occurred on submit
      1. Go to login page
      2. Fill in the two fields with valid information
      3. Submit login information
      4. Check login page for error message
  • Scenario 2
    • Good scenario
      1. Check title for login page
      2. Fill in the two fields with valid information
      3. Submit login page
      4. Check title for confirmation page
      5. Confirm that the content we entered was accepted
      6. Cancel confirmation page
      7. Confirm return to login page
      8. Confirm fields are filled in
    • Error scenario
      • None
  • Scenario 3
    • Good scenario
      1. Go to login page
      2. Fill in the two fields with valid information
      3. Submit login page
      4. Check title for confirmation page
      5. Submit confirmation page
      6. Confirm error page is display with associated message
    • Error scenario
      • None

Things we are not doing:

  • Testing field validation
  • Testing database behavior

It is not that we don’t care (well, I don’t but…), but rather that the code for that behavior should already have been tested. If we are doing integration testing we are just guaranteeing that the behavior we expect actually occurs. If the various tests have been done then we can be sure that things should work. However, things can still fail which is why running integration tests is so important.

The tests above are just what I came up with off the top of my head. Never be afraid to add more tests to your test suite, but remember: don’t test things you don’t have to.

How (are we doing it?)

Software Requirements

  1. Create a Java Project and name if jWebUnit-HiddenClausePrizeTest
  2. Add jWebUnit to the project through the project Preferences window
    • jwebunit-core-2.2.jar
    • jwebunit-htmlunit-plugin-2.2.jar
    • All of the JAR files under $JWEBUNIT/lib
  3. Create a JUnit Test Case
    • JUnit 4
    • Package: com.hiddenclause.jwebunit.example
    • Name: UseCaseRegistrationTest
  4. Scenario 1
    • Implement testScenario1EverythingWorks()
    • Implement testScenario1EmptyInputFields()
    • Implement testScenario1InvalidNameInputNumeric()
    • Implement testScenario1InvalidNameInputLength()
    • Implement testScenario1InvalidPasswordInputNumeric()
    • Implement testScenario1InvalidPasswordInputLength()
    • Implement testScenario1UnknownError()
  5. Scenario 2
    • Implement testScenario2UserReEntersRegistrationInformation()
  6. Scenario 3
    • Implement testScenario3SystemErrorOnConfirm()

I did not list the code above as it is all duplicated below.

Why (did we do it that way?)

With any luck you have already created the Java project with JUnit 4 and the various jWebUnit JAR files. If not, rewind, perform the first few steps of the How section and come back.

In TDD you are supposed to:

  • Write a test and watch it fail
  • Write the code to make the test pass and watch it pass
  • Refactor

In short: lather, rinse, repeat. As you write the tests the implementation code comes to life, you finish your project early and your manager showers you with riches and accolades and all will be right with the world.

Good luck with that last part.

So let’s test Scenario 1 of the use case.

Implement testScenario1EverythingWorks()

We start by writing a minimal setUp() method and a call to WebTester.beginAt() in testScenario1EverythingWorks().

UseCaseRegistrationTest.java

public class UseCaseRegistrationTest {
    private WebTester _webTester;

    @Before
    public void setUp() {
        _webTester = new WebTester();
        _webTester.setTestingEngineKey(TestingEngineRegistry.TESTING_ENGINE_HTMLUNIT);

        _webTester.setBaseUrl("http://localhost:8080/hiddenclause"); //$NON-NLS-1$
    }

    @Test
    public void testScenario1EverythingWorks() {
        _webTester.beginAt("/login.jsp");
    }
}

Run the above and watch it fail. Excellent! Not only does login.jsp not exist neither does the web context hiddenclause. Time to write the code that passes.

  • Create a Dynamic Web Project and name it (wait for it) hiddenclause
  • Assign your Tomcat 6.0 installation as the Target Runtime
  • Create a JSP page and name it login.jsp
  • Right click on hiddenclause –> WebContent –> login.jsp –> Run on Server and click Finish

The Eclipse web browser should open on an empty page. Perfect. Run the jWebUnit test again. The test passes. What? You call that a test? Well, yes; anytime you find bad behavior you are testing your expectations of the system; something goes wrong and you do something to fix it. This is all part of the interactivity of test-driven development. Embrace it.

Quick review: what are we trying to test in the flow for scenario 1:

  1. Check title for login page
  2. Fill in the two fields with valid information
  3. Submit login page
  4. Check title for confirmation page
  5. Confirm that the content we entered was accepted
  6. Submit confirmation page
  7. Check title for success page
  8. Confirm success message

Let’s check for the title:

UseCaseRegistrationTest.java

    @Test
    public void testScenario1EverythingWorks() {
        _webTester.beginAt("/login.jsp");

        _webTester.assertTitleEquals("Welcome! Register here!");
    }

Run the test. Hmm. Lots of red. Hmm. The title doesn’t appear to be in login.jsp. I guess we should put it in.

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Welcome! Register here!</title>
  </head>
  <body>

  </body>
</html>

Run the test. Should pass.

So the pattern will be: add code to the test until we hit an assertion. At that point:

  1. Run the test
  2. See the assert fail
  3. Update the JSP
  4. Run the test again
  5. See the JSP pass

What’s next:

  1. Fill in the two fields with valid information
  2. Submit login page
  3. Check title for confirmation page

UseCaseRegistrationTest.java

    @Test
    public void testScenario1EverythingWorks() {
        _webTester.beginAt("/login.jsp");

        _webTester.assertTitleEquals("Welcome! Register here!");

        String username = "Paul Revere";
        String password = "OneIfByLand";
        _webTester.setTextField("username", username);
        _webTester.setTextField("password", password);

        _webTester.submit();

        _webTester.assertTitleEquals("Confirm Registration Information");
    }

Run the test. To fix the failure update login.jsp with:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Welcome! Register here!</title>
  </head>
  <body>
    <form action="confirmation.jsp">
        <input name="username">
        <input name="password" type="hidden">
        <input type="submit" value="Submit">
    </form>
  </body>
</html>

Create confirmation.jsp and give it the proper title based on the test:

<%@ page language="java" contentType="text/html; charset=UTF-8"
  pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Confirm Registration Information</title>
  </head>
  <body>
  </body>
</html>

Run the test; you should pass.

What’s next:

  • Confirm that the content we entered was accepted
  • Submit confirmation page
  • Check title for success page
  • Confirm success message

UseCaseRegistrationTest.java

    @Test
    public void testScenario1EverythingWorks() {
	...

        _webTester.assertTitleEquals("Confirm Registration Information");

        _webTester.assertTextPresent(username);
        _webTester.assertTextPresent(password);

        _webTester.submit();

        _webTester.assertTitleEquals("Success!");
        _webTester.assertTextPresent("Thanks for registering!");
    }

Run the test. Fix the failure by updating confirmation.jsp with:

<%@ page language="java" contentType="text/html; charset=UTF-8"
  pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Confirm Registration Information</title>
  </head>
  <body>
    Name: <%=request.getParameter("username") %>
    Password: <%=request.getParameter("password") %>
    <form action="success.jsp">
        <input type="submit" value="Submit">
    </form>
  </body>
</html>

Also create success.jsp and change the title:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Success!</title>
  </head>
  <body>
    Thanks for registering!
  </body>
</html>

Run the test; you should pass.

I hope you have been finding the jWebUnit code interesting. It is almost script-like. I find that rather than write down the flow on a white board I can just about type it directly into the test and add the calls to the proper jWebUnit API. Very very cool (that’s two verys).

So let’s agree on a process: from here on down I will list the scenario, you will enter the test code, you will execute the test code, you will update the JSP and/or HTML, and you will run the test again. Agreed?

Okay. Let’s go.

Implement testScenario1EmptyInputFields()

Error scenario 1

  • Go to login page
  • Submit login page without filling in the fields
  • Check login page for error message

UseCaseRegistrationTest.java

    @Test
    public void testScenario1EmptyInputFields() {
        _webTester.beginAt("/login.jsp");

        _webTester.submit();

        _webTester.assertTitleEquals("Welcome! Register here!");
        _webTester.assertTextPresent("All fields are required! Try again! Don't make me go back there!");
    }

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Welcome! Register here!</title>
  </head>
  <body>
  <%
  String msg = null;
  String submit = request.getParameter("submit");
  if (submit != null && submit.equals("Submit")) {
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    if (username == null
            || username.trim().length() == 0
            || password == null
            || password.trim().length() == 0) {
        msg = "All fields are required! Try again! Don't make me go back there!";
    }

    if (msg == null) {
        request.getRequestDispatcher("confirmation.jsp").forward(request, response);
    } else {
    %>
      <span style="color: red;"><%=msg %></span>
    <%
    }
  }
  %>
    <form action="login.jsp">
        <input name="username">
        <input name="password" type="hidden">
        <input type="submit" value="Submit" name="submit">
    </form>
  </body>
</html>

Implement testScenario1InvalidNameInputNumeric()

Error scenario 2

  • Go to login page
  • Fill in the name field with invalid information
  • Submit page login page
  • Check login page for error message

UseCaseRegistrationTest.java

    @Test
    public void testScenario1InvalidNameInputNumeric() {
        _webTester.beginAt("/login.jsp");

        _webTester.setTextField("username", "thx1138");
        _webTester.setTextField("password", "hi");
        _webTester.submit();

        _webTester.assertTitleEquals("Welcome! Register here!");
        _webTester.assertTextPresent("Yo! The username field can only contain letters!");
    }

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@page import="java.util.regex.Pattern"%>
<%@page import="java.util.regex.Matcher"%>
<%!
 private Pattern _pattern = Pattern.compile("[0-9]");
%>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Welcome! Register here!</title>
  </head>
  <body>
  <%
  String msg = null;
  String submit = request.getParameter("submit");
  if (submit != null && submit.equals("Submit")) {
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    if (username == null
            || username.trim().length() == 0
            || password == null
            || password.trim().length() == 0) {
        msg = "All fields are required! Try again! Don't make me go back there!";
    } else {
      Matcher matcher = _pattern.matcher(username);
      if (matcher.find()) {
          msg = "Yo! The username field can only contain letters!";
      }
    }

    if (msg == null) {
        request.getRequestDispatcher("confirmation.jsp").forward(request, response);
    } else {
    %>
      <span style="color: red;"><%=msg %></span>
    <%
    }
  }
  %>
    <form action="login.jsp">
        <input name="username">
        <input name="password" type="hidden">
        <input type="submit" value="Submit" name="submit">
    </form>
  </body>
</html>

Implement testScenario1InvalidNameInputLength()

UseCaseRegistrationTest.java

UseCaseRegistrationTest.java

    @Test
    public void testScenario1InvalidNameInputLength() {
        _webTester.beginAt("/login.jsp");

        _webTester.setTextField("username", "abcdefghijklmnopqrstuvwxyzABCDE");
        _webTester.setTextField("password", "hi");
        _webTester.submit();

        _webTester.assertTitleEquals("Welcome! Register here!");
        _webTester.assertTextPresent("Yo! The username can only be 30 letters or less!");
    }

login.jsp

  <%
  String msg = null;
  String submit = request.getParameter("submit");
  if (submit != null && submit.equals("Submit")) {
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    if (username == null
            || username.trim().length() == 0
            || password == null
            || password.trim().length() == 0) {
        msg = "All fields are required! Try again! Don't make me go back there!";
    } else {
      Matcher matcher = _pattern.matcher(username);
      if (matcher.find()) {
          msg = "Yo! The username can only contain letters!";
      }

      if (username.trim().length() > 30) {
          msg = "Yo! The username can only be 30 letters or less!";
      }
    }

    if (msg == null) {
        request.getRequestDispatcher("confirmation.jsp").forward(request, response);
    } else {
    %>
      <span style="color: red;"><%=msg %></span>
    <%
    }
  }
  %>

Implement testScenario1InvalidPasswordInputNumeric()

Error scenario 2

  • Go to login page
  • Fill in the password fields with invalid information
  • Submit login page
  • Check login page for error message

UseCaseRegistrationTest.java

    /*
     * The code for these methods is the same. Refactored them.
     */
    @Test
    public void testScenario1InvalidNameInputNumeric() {
        assertNumericField("thx1138", "hi", "Yo! The username can only contain letters!");
    }

    @Test
    public void testScenario1InvalidPasswordInputNumeric() {
        assertNumericField("Paul Revere", "thx1138", "Yo! The password can only contain letters!");
    }

    private void assertNumericField(String username, String password,
                                    String errMsg) {
        _webTester.beginAt("/login.jsp");

        _webTester.setTextField("username", username);
        _webTester.setTextField("password", password);
        _webTester.submit();

        _webTester.assertTitleEquals("Welcome! Register here!");
        _webTester.assertTextPresent(errMsg);
    }

login.jsp

  <%
  String msg = null;
  String submit = request.getParameter("submit");
  if (submit != null && submit.equals("Submit")) {
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    if (username == null
            || username.trim().length() == 0
            || password == null
            || password.trim().length() == 0) {
        msg = "All fields are required! Try again! Don't make me go back there!";
    } else {
      Matcher matcher = _pattern.matcher(username);
      if (matcher.find()) {
          msg = "Yo! The username can only contain letters!";
      } else if (username.trim().length() > 30) {
          msg = "Yo! The username can only be 30 letters or less!";
      } else {
          matcher = _pattern.matcher(password);
          if (matcher.find()) {
              msg = "Yo! The password can only contain letters!";
          }
      }
    }

    if (msg == null) {
        request.getRequestDispatcher("confirmation.jsp").forward(request, response);
    } else {
    %>
      <span style="color: red;"><%=msg %></span>
    <%
    }
  }
  %>

Implement testScenario1InvalidPasswordInputLength()

UseCaseRegistrationTest.java

    /*
     * The code for these methods is the same. Refactored them.
     */
    @Test
    public void testScenario1InvalidNameInputLength() {
        assertFieldLengthErrorFound("abcdefghijklmnopqrstuvwxyzABCDE", "hi", "Yo! The username can only be 30 letters or less!");
    }

    @Test
    public void testScenario1InvalidPasswordInputLength() {
        assertFieldLengthErrorFound("Paul Revere", "abcdefghijklmnopqrstuvwxyzABCDE", "Yo! The password can only be 30 letters or less!");
    }

    private void assertFieldLengthErrorFound(String username, String password,
                                             String errMsg) {
        _webTester.beginAt("/login.jsp");

        _webTester.setTextField("username", username);
        _webTester.setTextField("password", password);
        _webTester.submit();

        _webTester.assertTitleEquals("Welcome! Register here!");
        _webTester.assertTextPresent(errMsg);
    }

login.jsp

  <%
  String msg = null;
  String submit = request.getParameter("submit");
  if (submit != null && submit.equals("Submit")) {
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    if (username == null
            || username.trim().length() == 0
            || password == null
            || password.trim().length() == 0) {
        msg = "All fields are required! Try again! Don't make me go back there!";
    } else {
      Matcher matcher = _pattern.matcher(username);
      if (matcher.find()) {
          msg = "Yo! The username can only contain letters!";
      } else if (username.trim().length() > 30) {
          msg = "Yo! The username can only be 30 letters or less!";
      } else {
          matcher = _pattern.matcher(password);
          if (matcher.find()) {
              msg = "Yo! The password can only contain letters!";
          } else if (password.trim().length() > 30) {
              msg = "Yo! The password can only be 30 letters or less!";
          }
      }
    }

    if (msg == null) {
        request.getRequestDispatcher("confirmation.jsp").forward(request, response);
    } else {
    %>
      <span style="color: red;"><%=msg %></span>
    <%
    }
  }
  %>

Implement testScenario1UnknownErrorOnSubmit()

Scenario 1 – Unknown error on submit

  1. Go to login page
  2. Fill in the two fields with valid information
  3. Submit login page
  4. Check login page for error message

UseCaseRegistrationTest.java

    @Test
    public void testScenario1UnknownErrorOnSubmit() {
        _webTester.beginAt("/login.jsp");

        String username = "Force an error";
        String password = "VeryBad";
        _webTester.setTextField("username", username);
        _webTester.setTextField("password", password);

        _webTester.submit();

        _webTester.assertTitleEquals("Welcome! Register here!");
        _webTester.assertTextPresent("I'm sorry, but we seem to have encountered an error. Please try again later or contact Hidden Clause customer support.");
    }

login.jsp

  <%
  String msg = null;
  String submit = request.getParameter("submit");
  String username = request.getParameter("username");
  String password = request.getParameter("password");
  if (submit != null && submit.equals("Submit")) {
    if (username == null
            || username.trim().length() == 0
            || password == null
            || password.trim().length() == 0) {
        msg = "All fields are required! Try again! Don't make me go back there!";
    } else if (username.equals("Force an error")) {
	// This else if would normally not be here. This is here to force an error
	// so we can check if our code can handle it.
        msg = "I'm sorry, but we seem to have encountered an error. Please try again later or contact Hidden Clause customer support.";
    } else {
      Matcher matcher = _pattern.matcher(username);
      if (matcher.find()) {
          msg = "Yo! The username can only contain letters!";
      } else if (username.trim().length() > 30) {
          msg = "Yo! The username can only be 30 letters or less!";
      } else {
          matcher = _pattern.matcher(password);
          if (matcher.find()) {
              msg = "Yo! The password can only contain letters!";
          } else if (password.trim().length() > 30) {
              msg = "Yo! The password can only be 30 letters or less!";
          }
      }
    }

    if (msg == null) {
        request.getRequestDispatcher("confirmation.jsp").forward(request, response);
    } else {
    %>
      <span style="color: red;"><%=msg %></span>
    <%
    }
  }
  %>

Implement testScenario2UserReEntersRegistrationInformation()

Scenario 2
Visitor changes registration information

  • Go to login page
  • Fill in the two fields with valid information
  • Submit login page
  • Check title for confirmation page
  • Cancel confirmation page
  • Confirm return to login page
  • Confirm fields are filled in

UseCaseRegistrationTest.java

    @Test
    public void testScenario2UserReEntersRegistrationInformation() {
        _webTester.beginAt("/login.jsp");

        String username = "Paul Revere";
        String password = "OneIfByLand";
        _webTester.setTextField("username", username);
        _webTester.setTextField("password", password);
        _webTester.submit();

        _webTester.assertTitleEquals("Confirm Registration Information");
        _webTester.submit("edit");

        _webTester.assertTitleEquals("Welcome! Register here!");
        _webTester.assertTextFieldEquals("username", username);
        _webTester.assertTextFieldEquals("password", password);
    }

login.jsp

  <body>
  <%
  String msg = null;
  String submit = request.getParameter("submit");
  String username = request.getParameter("username");
  String password = request.getParameter("password");
  if (submit != null && submit.equals("Submit")) {
    if (username == null
            || username.trim().length() == 0
            || password == null
            || password.trim().length() == 0) {
        msg = "All fields are required! Try again! Don't make me go back there!";
    } else if (username.equals("Force an error")) {
        msg = "I'm sorry, but we seem to have encountered an error. Please try again later or contact Hidden Clause customer support.";
    } else {
      Matcher matcher = _pattern.matcher(username);
      if (matcher.find()) {
          msg = "Yo! The username can only contain letters!";
      } else if (username.trim().length() > 30) {
          msg = "Yo! The username can only be 30 letters or less!";
      } else {
          matcher = _pattern.matcher(password);
          if (matcher.find()) {
              msg = "Yo! The password can only contain letters!";
          } else if (password.trim().length() > 30) {
              msg = "Yo! The password can only be 30 letters or less!";
          }
      }
    }

    if (msg == null) {
        request.getRequestDispatcher("confirmation.jsp").forward(request, response);
    } else {
    %>
      <span style="color: red;"><%=msg %></span>
    <%
    }
  }
  %>
    <form action="login.jsp">
        <input name="username" value='<%=username != null ? username : "" %>'>
        <input name="password" type="hidden" value='<%= password != null ? password : "" %>'>
        <input type="submit" value="Submit" name="submit">
    </form>
  </body>

confirmation.jsp

  <body>
    <%
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    %>
    Name: <%= username %>
    Password: <%= password %>
    <form action="success.jsp">
      <input type="submit" value="Submit">
    </form>

    <form action="login.jsp?">
      <input type="hidden" name="username" value="<%=username %>">
      <input type="hidden" name="password" value="<%=password %>">
      <input type="submit" name="edit" value="Edit">
    </form>
  </body>

Implement testScenario3SystemErrorOnConfirm()

Scenario on system error

  • Go to login page
  • Fill in the two fields with valid information
  • Submit login page
  • Check title for confirmation page
  • Submit confirmation page
  • Confirm error page is display with associated message

UseCaseRegistrationTest.java

    @Test
    public void testScenario3SystemErrorOnConfirm() {
        _webTester.beginAt("/login.jsp");

        String username = "Confirmation error";
        String password = "OneIfByLand";
        _webTester.setTextField("username", username);
        _webTester.setTextField("password", password);
        _webTester.submit();

        _webTester.assertTitleEquals("Confirm Registration Information");
        _webTester.submit();

        assertLoginPageWithErrorMessage("I'm sorry, but we seem to have encountered an error. Please try again later or contact Hidden Clause customer support.");
    }

login.jsp

  <%
  String msg = null;
  String submit = request.getParameter("submit");
  String username = request.getParameter("username");
  String password = request.getParameter("password");
  if (submit != null && submit.equals("Submit")) {
    if (username == null
            || username.trim().length() == 0
            || password == null
            || password.trim().length() == 0) {
        msg = "All fields are required! Try again! Don't make me go back there!";
    } else if (username.equals("Force an error")) {
        msg = "I'm sorry, but we seem to have encountered an error. Please try again later or contact Hidden Clause customer support.";
    } else {
      Matcher matcher = _pattern.matcher(username);
      if (matcher.find()) {
          msg = "Yo! The username can only contain letters!";
      } else if (username.trim().length() > 30) {
          msg = "Yo! The username can only be 30 letters or less!";
      } else {
          matcher = _pattern.matcher(password);
          if (matcher.find()) {
              msg = "Yo! The password can only contain letters!";
          } else if (password.trim().length() > 30) {
              msg = "Yo! The password can only be 30 letters or less!";
          }
      }
    }

    if (msg == null) {
        request.getRequestDispatcher("confirmation.jsp").forward(request, response);
    } else {
    %>
      <span style="color: red;"><%=msg %></span>
    <%
    }
  }
  %>

confirmation.jsp

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Confirm Registration Information</title>
  </head>
  <body>
    <%
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    %>
    Name: <%= username %>
    Password: <%= password %>
    <form action="success.jsp">
      <input type="hidden" name="username" value="<%=username %>">
      <input type="hidden" name="password" value="<%=password %>">
      <input type="submit" value="Submit">
    </form>

    <form action="login.jsp?">
      <input type="hidden" name="username" value="<%=username %>">
      <input type="hidden" name="password" value="<%=password %>">
      <input type="submit" name="edit" value="Edit">
    </form>
  </body>
</html>

success.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
String username = request.getParameter("username");
if (username.equals("Confirmation error")) {
    request.getRequestDispatcher("login.jsp?username=Force%20an%20error&submit=Submit").forward(request, response);
}
%>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Success!</title>
  </head>
  <body>

  </body>
</html>

Notice how things that we tested previously don’t get tested again (empty input fields, invalid input). Just because an error can happen across scenarios doesn’t mean we have to test it in each scenario.

jWebUnit has a great API. It mixes a little testing (assertTitleEquals()) with a little flow (clickLink()).

What Just Happened?

I know, I know! The actual HTML pages are ugly, ugly, ugly! You can’t even see the two input fields side by side to see if they are really there; but they are really there because the tests passed.

That’s not a problem! The only thing we care about is the flow. We were able to prove that we can go from page to page based on the decisions made by the user or caused by the system. Let some high-priced GUI person step in and design some kick-ass screen that will make Minority Report envious.

Otherwise, wasn’t that interesting? That is quite a bit of code to test our flow and doesn’t really take into account all sorts of other scenarios. But that’s okay! Really. Remember that the pages above are fake…meaning that you would not normally have hard-coded Java in a JSP anyway. I used it just to get my tests up and running; normally you would have JSP tags and/or calls to servlet/Struts/WebWork/Tapestry code that would do all the real work (like validating the input).

What web testing framework are you using? Care to share?

The full code for this is below because I am sure that I left something out somewhere. Remember: not only did I develop this iteratively, I wrote it that way too. Not always a recipe for success.

Thanks to Ken Kranz for suggesting this topic.

Questions

I’m confused! If we are not supposed to test functionality that has been tested previously then why did we test input handling (input length and letters-only)?

Great question! By rights if you are using a web framework like Struts or WebWorks you should already have tests in place that would check for things like missing or incorrect values. In a truly secure application you would have JavaScript doing validation on the client-side and then do the exact same checks again on the server-side just to be sure that someone isn’t trying to get around your security. If you use a web framework and you don’t want to have duplicate validation then, no, you would not have tested input validation within jWebUnit.

From where did Molotov cocktails get their name?

They are called Molotov cocktails after Vyacheslav Molotov, the Commissar for Foreign Affairs of the Soviet Union, during World War II. Wikipedia has this to say about Molotov cocktails:

During the [World War II] Winter War, the Soviet air force made extensive use of incendiaries and cluster bombs against Finnish troops and fortifications. When Soviet People’s Commissar for Foreign Affairs Vyacheslav Molotov claimed in radio broadcasts that the Soviet Union was not dropping bombs but rather delivering food to the starving Finns, the Finns started to call the air bombs Molotov bread baskets. Soon they responded by attacking advancing tanks with “Molotov cocktails” which were “a drink to go with the food”.

Can’t make this stuff up.

[What? You were expecting questions related to jWebUnit? Oh, c’mon!]

References

Test Web applications with HttpUnit is a great article on unit testing your web application and, even though it was written in 2004 (there were people back then?) does a great job of discussing the sorts of architectural and philosophical things you should consider as you add (more) testing to your process.

Tools For Unit Testing Java Web Applications

Unit Testing Web Applications

JUnitDoclet

Continuous Integration
Team City

Code

UseCaseRegistrationTest.java

/**
 * Coder beware: this code is not warranted to do anything.
 *
 * Copyright Dec 12, 2009 Carlos Valcarcel
 */
package com.hiddenclause.jwebunit.example;

import net.sourceforge.jwebunit.junit.WebTester;
import net.sourceforge.jwebunit.util.TestingEngineRegistry;

import org.junit.Before;
import org.junit.Test;


/**
 * @author carlos
 *
 */
public class UseCaseRegistrationTest {
    private WebTester _webTester;

    @Before
    public void setUp() {
        _webTester = new WebTester();
        _webTester.setTestingEngineKey(TestingEngineRegistry.TESTING_ENGINE_HTMLUNIT);
        
        _webTester.setBaseUrl("http://localhost:8080/hiddenclause"); //$NON-NLS-1$
    }

    @Test
    public void testScenario1EverythingWorks() {
        _webTester.beginAt("/login.jsp");
        
        _webTester.assertTitleEquals("Welcome! Register here!");

        String username = "Paul Revere";
        String password = "OneIfByLand";
        _webTester.setTextField("username", username);
        _webTester.setTextField("password", password);
        
        _webTester.submit();
        
        _webTester.assertTitleEquals("Confirm Registration Information");
        
        _webTester.assertTextPresent(username);
        _webTester.assertTextPresent(password);
        
        _webTester.submit();
        
        _webTester.assertTitleEquals("Success!");
        _webTester.assertTextPresent("Thanks for registering!");
    }
    
    @Test
    public void testScenario1EmptyInputFields() {
        _webTester.beginAt("/login.jsp");
        
        _webTester.submit();
        
        assertLoginPageWithErrorMessage("All fields are required! Try again! Don't make me go back there!");
    }
    
    /*
     * The code for these methods is the same. Refactored them.
     */
    @Test
    public void testScenario1InvalidNameInputNumeric() {
        assertFieldNumericErrorFound("thx1138", "hi", "Yo! The username can only contain letters!");
    }
    
    @Test
    public void testScenario1InvalidPasswordInputNumeric() {
        assertFieldNumericErrorFound("Paul Revere", "thx1138", "Yo! The password can only contain letters!");
    }
    
    private void assertFieldNumericErrorFound(String username, String password,
                                    String errMsg) {
        _webTester.beginAt("/login.jsp");
        
        _webTester.setTextField("username", username);
        _webTester.setTextField("password", password);
        _webTester.submit();
        
        assertLoginPageWithErrorMessage(errMsg);
    }

    /*
     * The code for these methods is the same. Refactored them.
     */
    @Test
    public void testScenario1InvalidNameInputLength() {
        assertFieldLengthErrorFound("abcdefghijklmnopqrstuvwxyzABCDE", "hi", "Yo! The username can only be 30 letters or less!");
    }
    
    @Test
    public void testScenario1InvalidPasswordInputLength() {
        assertFieldLengthErrorFound("Paul Revere", "abcdefghijklmnopqrstuvwxyzABCDE", "Yo! The password can only be 30 letters or less!");
    }
    
    private void assertFieldLengthErrorFound(String username, String password,
                                             String errMsg) {
        _webTester.beginAt("/login.jsp");
        
        _webTester.setTextField("username", username);
        _webTester.setTextField("password", password);
        _webTester.submit();
        
        assertLoginPageWithErrorMessage(errMsg);
    }
    
    @Test
    public void testScenario1UnknownErrorOnSubmit() {
        _webTester.beginAt("/login.jsp");
        
        String username = "Force an error";
        String password = "VeryBad";
        _webTester.setTextField("username", username);
        _webTester.setTextField("password", password);

        _webTester.submit();
        
        assertLoginPageWithErrorMessage("I'm sorry, but we seem to have encountered an error. Please try again later or contact Hidden Clause customer support.");
    }
    
    @Test
    public void testScenario2UserReEntersRegistrationInformation() {
        _webTester.beginAt("/login.jsp");
        
        String username = "Paul Revere";
        String password = "OneIfByLand";
        _webTester.setTextField("username", username);
        _webTester.setTextField("password", password);
        _webTester.submit();
        
        _webTester.assertTitleEquals("Confirm Registration Information");
        _webTester.submit("edit");
        
        _webTester.assertTitleEquals("Welcome! Register here!");
        _webTester.assertTextFieldEquals("username", username);
        _webTester.assertTextFieldEquals("password", password);
    }

    @Test
    public void testScenario3SystemErrorOnConfirm() {
        _webTester.beginAt("/login.jsp");
        
        String username = "Confirmation error";
        String password = "OneIfByLand";
        _webTester.setTextField("username", username);
        _webTester.setTextField("password", password);
        _webTester.submit();
        
        _webTester.assertTitleEquals("Confirm Registration Information");
        _webTester.submit();

        assertLoginPageWithErrorMessage("I'm sorry, but we seem to have encountered an error. Please try again later or contact Hidden Clause customer support.");
    }
    
    private void assertLoginPageWithErrorMessage(String errorMessage) {
        _webTester.assertTitleEquals("Welcome! Register here!");
        _webTester.assertTextPresent(errorMessage);
    }

}

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@page import="java.util.regex.Pattern"%>
<%@page import="java.util.regex.Matcher"%>
<%!
 private Pattern _pattern = Pattern.compile("[0-9]");
%>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Welcome! Register here!</title>
  </head>
  <body>
  <%
  String msg = null;
  String submit = request.getParameter("submit");
  String username = request.getParameter("username");
  String password = request.getParameter("password"); 
  if (submit != null && submit.equals("Submit")) {
    if (username == null
            || username.trim().length() == 0
            || password == null
            || password.trim().length() == 0) {
        msg = "All fields are required! Try again! Don't make me go back there!";
    } else if (username.equals("Force an error")) {
        msg = "I'm sorry, but we seem to have encountered an error. Please try again later or contact Hidden Clause customer support.";
    } else {
      Matcher matcher = _pattern.matcher(username);
      if (matcher.find()) {
          msg = "Yo! The username can only contain letters!";
      } else if (username.trim().length() > 30) {
          msg = "Yo! The username can only be 30 letters or less!";
      } else {
          matcher = _pattern.matcher(password);
          if (matcher.find()) {
              msg = "Yo! The password can only contain letters!";
          } else if (password.trim().length() > 30) {
              msg = "Yo! The password can only be 30 letters or less!";
          }
      }
    }
  
    if (msg == null) {
        request.getRequestDispatcher("confirmation.jsp").forward(request, response);
    } else {
    %>
      <span style="color: red;"><%=msg %></span>
    <%
    }
  }
  %>
    <form action="login.jsp">
        <input name="username" value='<%=username != null ? username : "" %>'>
        <input name="password" type="hidden" value='<%= password != null ? password : "" %>'>
        <input type="submit" value="Submit" name="submit">
    </form>
  </body>
</html>

confirmation.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
  pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Confirm Registration Information</title>
  </head>
  <body>
    <%
    String username = request.getParameter("username");
    String password = request.getParameter("password"); 
    %>
    Name: <%= username %>
    Password: <%= password %>
    <form action="success.jsp">
      <input type="hidden" name="username" value="<%=username %>">
      <input type="hidden" name="password" value="<%=password %>">
      <input type="submit" value="Submit">
    </form>
    
    <form action="login.jsp?">
      <input type="hidden" name="username" value="<%=username %>">
      <input type="hidden" name="password" value="<%=password %>">
      <input type="submit" name="edit" value="Edit">
    </form>
  </body>
</html>

success.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
String username = request.getParameter("username");
if (username.equals("Confirmation error")) {
    request.getRequestDispatcher("login.jsp?username=Force%20an%20error&submit=Submit").forward(request, response);
}
%>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Success!</title>
  </head>
  <body>
    Thanks for registering!
  </body>
</html>

DBUnit in Eclipse

September 5, 2009 4 comments

Just the other day I was wondering how DBUnit was doing. As a former consultant I used to use DBUnit along with various JUnit extensions on a regular basis.

Given that Eclipse has moved on, JUnit has moved on and DBUnit has moved on I thought I would present a straightforward example of how to use DBUnit with JUnit 4.0 and Eclipse.

Not that much has changed therefore there is not going to be a lot of hand holding here.

Assumptions

Eclipse 3.5
JUnit 4.0 – included with Eclipse
DBUnit 2.4.5
SLF4J 1.5.8 – DBUnit needs this
HSQL DB 1.8.0

I implemented this example on Kubuntu 9.10, if that makes any difference.

If you are new to Eclipse then just download any version that seems reasonable as long as it includes a Java development environment.

The Easy Part

Make sure all of the above software is available somewhere on your machine. If not, install all the software in your favorite places.

Start Eclipse.

The Short Version

  1. Start your database
  2. Create a Java Project
  3. Add DBUnit to your classpath
  4. Write and run a database test
    • Create initial and expected dataset files
    • Extend DBTestCase (inheritance) or use a JUnit class (composition)
    • Implement your test methods

The Longer Version

Start your database

I don’t have a database to run so I downloaded and installed HSQL. To run the HSQL server, which I prefer in examples, open a command window, go to the HSQL folder and run:

java -cp lib/hsqldb.jar org.hsqldb.Server -database.0 file:hiddenclause -dbname.0 xdb

In this case the database name is xdb with the database files named hiddenclause.*. Call your files whatever. I will add test data later.

My Eclipse default configuration includes:
Source folder name: src
Output folder name: classes

Default execution environment: JavaSE-1.6

Create a Java Project

Create a Java Project named DBUnitExample. ‘Nuff said.

Add DBUnit to your classpath

Once the project appears in the Package Explorer, right click on the project name and select Properties –> Java Build Path –> Libraries. Click Add External JARs and add the DBUnit JAR file, in this case dbunit-2.4.5.jar, to the list of libraries in the classpath. Yes, you could also have done this when you first created the project.

Add:
slf4j-api-1.5.8.jar
slf4j-simple-1.5.8.jar
hsqldb.jar
to the classpath as well.

Click OK to close the Properties window.

Write and run a database test

Add Test Data

As running a test on a fresh database is a little difficult start the HSQL Database Manager from another shell (in the HSQL directory):

java -cp lib/hsqldb.jar org.hsqldb.util.DatabaseManager

In the Connect window enter:
Setting Name: hiddenclause example
Type: HSQL Database Engine Server
Driver: org.hsqldb.jdbcDriver
URL: jdbc:hsqldb:hsql://localhost/xdb
User: sa
Password: [leave blank]

Click OK.

Almost done. Select Options –> Insert Test Data. Now we have 4 tables worth of data to test with. Run a delete on the CUSTOMER table so that is is empty.

Close the Database Manager.

Write a Database Test

The steps for writing a DBUnit test are:
1. Create initial and expected dataset files
2. Extend DBTestCase (inheritance) or use a JUnit class (composition)
3. Implement your test methods

Once you get comfortable with that the additional steps are:
1. Create initial and expected dataset files
2. Extend DBTestCase (inheritance) or use a JUnit class (composition)
3. Implement getSetUpOperation() and getTearDownOperation() (optional)
4. Override setUpDatabaseConfig() (optional)
5. Implement your test methods

We’ll just do the first one using the test data created by HSQL.

Create initial and expected dataset files

The DBUnit dataset can come from anywhere (files, databases, spreadsheets, etc.). Where the data comes from is hidden behind the class that implements IDataSet. For this example, we will use XML datasets.

Here is the initial dataset file:
customer-init.xml

<?xml version="1.0" encoding="UTF-8"?>
<dataset.
    <CUSTOMER />
</dataset>

Here is the expected dataset (what we expect to find in the database after executing some code):

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
     <CUSTOMER ID="1"
               FIRSTNAME="John"
               LASTNAME="Smith"
               STREET="1 Main Street"
               CITY="Anycity" />
</dataset>
Extend DBTestCase (inheritance) or use a JUnit class (composition)

The first version of CustomerTest will inherit from the DBUnit class DBTestCase. That is the recommended way of creating a DBUnit test. It uses the JUnit 3.8.2 classes which still works even with the JUnit 4.0 JAR file.

public class CustomerTest extends DBTestCase {
...
    @Override
    protected IDataSet getDataSet() throws Exception {
        ...
    }
}

The getDataSet() method is called to initialize the database before the test. Consider it part of your setup logic. Let’s load the initialization dataset.

    @Override
    protected IDataSet getDataSet() throws Exception {
        return new FlatXmlDataSet(
                 new FileInputStream("customer-init.xml"));
    }

There are a number of properties that need to be set prior to DBUnit doing its magic. You can set those properties in the constructor:

    public CustomerTest(String name) {
        super(name);
        System.setProperty(
          PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS,
          "org.hsqldb.jdbcDriver");
        System.setProperty(
          PropertiesBasedJdbcDatabaseTester.DBUNIT_CONNECTION_URL,
          "jdbc:hsqldb:hsql://localhost/xdb");
        System.setProperty(
          PropertiesBasedJdbcDatabaseTester.DBUNIT_USERNAME,
          "sa");
        System.setProperty(
          PropertiesBasedJdbcDatabaseTester.DBUNIT_PASSWORD,
          "");

        _customerFactory = CustomerFactory.getInstance();
    }

In real life you would set the driver name, connection URL and username and password to their appropriate values.

Implement your test methods

For this example, we are going to test an insert into the db.

    public void testInsert() throws Exception {
        // insert a customer into the database
        Customer customer = _customerFactory.create("John", "Smith");
        customer.setStreet("1 Main Street");
        customer.setCity("Anycity");
        _customerFactory.update(customer);
...

The code for the CustomerFactory and Customer are at the end of this post.

The data that has just been entered into the database becomes your actual assertable values. Go get them.

        // get the actual table values
        IDatabaseConnection connection = getConnection();
        IDataSet databaseDataSet = connection.createDataSet();
        ITable actualTable = databaseDataSet.getTable("CUSTOMER");

The values defined in customer-expected.xml are what you expect the values to be. Go get them.

        // get the expected table values
        IDataSet expectedDataSet = new FlatXmlDataSet(
                                          new FileInputStream("customer-expected.xml"));
        ITable expectedTable = expectedDataSet.getTable("CUSTOMER");

Check the actual against the expected and complain or not as the case may be.
Assertion.assertEquals(expectedTable, actualTable);
}[/sourcecode]
A version that uses a JUnit class as a wrapper around the DBUnit code looks like this:

/**
 * This is an example only! Use it for anything else at your own risk!
 * You have been warned! Coder/user beware!
 *
 * copyright 2009 Carlos Valcarcel
 */
package hiddenclause.example.dbunit;

import java.io.FileInputStream;

import org.dbunit.Assertion;
import org.dbunit.IDatabaseTester;
import org.dbunit.JdbcDatabaseTester;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * @author carlos
 */
public class CustomerJunitTest {

    private CustomerFactory _customerFactory;

    private IDatabaseTester databaseTester;

    @Before
    public void setUp() throws Exception {
        databaseTester = new JdbcDatabaseTester("org.hsqldb.jdbcDriver",
                                                "jdbc:hsqldb:hsql://localhost/xdb",
                                                "sa", "");
        // initialize your dataset here
        IDataSet dataSet = new FlatXmlDataSet(new FileInputStream("customer-init.xml"));

        databaseTester.setDataSet(dataSet);

        // will call default setUpOperation
        databaseTester.onSetup();

        _customerFactory = CustomerFactory.getInstance();
    }

    @Test
    public void testInsert() throws Exception {
        // insert a customer into the database
        Customer customer = _customerFactory.create("John", "Smith");
        customer.setStreet("1 Main Street");
        customer.setCity("Anycity");
        _customerFactory.update(customer);

        // get the actual table values
        IDatabaseConnection connection = databaseTester.getConnection();
        IDataSet databaseDataSet = connection.createDataSet();
        ITable actualTable = databaseDataSet.getTable("CUSTOMER");

        // get the expected table values
        IDataSet expectedDataSet = new FlatXmlDataSet(
                                          new FileInputStream("customer-expected.xml"));
        ITable expectedTable = expectedDataSet.getTable("CUSTOMER");

        Assertion.assertEquals(expectedTable, actualTable);

    }

    @After
    public void tearDown() throws Exception {
        databaseTester.onTearDown();
    }
}

Things to notice:
– less configuration (the System.setProperty() calls are gone)
– explicit creation of a IDatabaseTester object
– explicit call to databaseTester.onSetup()
– explicit call to databaseTester.onTearDown()

Run the Database Test

With all the pieces in place it is now safe to run the CustomerTest DBUnit class. You will probably see some warning messages in the Console view about the data type factory being incorrect. You can safely ignore that error for this example. In real life you probably want to instantiate a new DataTypeFactory based on the database you are using.

If any of the above does not quite work as described let me know and I will update the above explanation.

The Code

customer-init.xml

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <CUSTOMER />
</dataset>

customer-expected.xml

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <CUSTOMER ID="1"
              FIRSTNAME="John"
              LASTNAME="Smith"
              STREET="1 Main Street"
              CITY="Anycity" />
</dataset>

CustomerTest.java

/**
 * This is an example only! Use it for anything else at your own risk!
 * You have been warned! Coder/user beware!
 *
 * copyright 2009 Carlos Valcarcel
 */
package hiddenclause.example.dbunit;

import java.io.FileInputStream;

import org.dbunit.Assertion;
import org.dbunit.DBTestCase;
import org.dbunit.PropertiesBasedJdbcDatabaseTester;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.xml.FlatXmlDataSet;

/**
 * @author carlos
 */
public class CustomerTest extends DBTestCase {

    private CustomerFactory _customerFactory;

    public CustomerTest(String name) {
        super(name);
        System.setProperty(
          PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS,
          "org.hsqldb.jdbcDriver");
        System.setProperty(
          PropertiesBasedJdbcDatabaseTester.DBUNIT_CONNECTION_URL,
          "jdbc:hsqldb:hsql://localhost/xdb");
        System.setProperty(
          PropertiesBasedJdbcDatabaseTester.DBUNIT_USERNAME,
          "sa");
        System.setProperty(
          PropertiesBasedJdbcDatabaseTester.DBUNIT_PASSWORD,
          "");

        _customerFactory = CustomerFactory.getInstance();
    }

    public void testInsert() throws Exception {
        // insert a customer into the database
        Customer customer = _customerFactory.create("John", "Smith");
        customer.setStreet("1 Main Street");
        customer.setCity("Anycity");
        _customerFactory.update(customer);

        // get the actual table values
        IDatabaseConnection connection = getConnection();
        IDataSet databaseDataSet = connection.createDataSet();
        ITable actualTable = databaseDataSet.getTable("CUSTOMER");

        // get the expected table values
        IDataSet expectedDataSet = new FlatXmlDataSet(
                                          new FileInputStream("customer-expected.xml"));
        ITable expectedTable = expectedDataSet.getTable("CUSTOMER");

        Assertion.assertEquals(expectedTable, actualTable);

    }
    /*
     * (non-Javadoc)
     * @see org.dbunit.DatabaseTestCase#getDataSet()
     */
    @Override
    protected IDataSet getDataSet() throws Exception {
        return new FlatXmlDataSet(
                 new FileInputStream("customer-init.xml"));
    }

}

CustomerFactory.java

/**
 * This is an example only! Use it for anything else at your own risk!
 * You have been warned! Coder/user beware!
 *
 * copyright 2009 Carlos Valcarcel
 */
package hiddenclause.example.dbunit;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * @author carlos
 *
 */
public class CustomerFactory {

    static {
        try {
            Class.forName("org.hsqldb.jdbcDriver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static CustomerFactory getInstance()
    {
        return new CustomerFactory();
    }

    public Customer create(String firstName, String lastName) {
        return new Customer(1, firstName, lastName);
    }

    public void update(Customer customer) throws SQLException {
        Connection connection = DriverManager.getConnection("jdbc:hsqldb:hsql://localhost/xdb");
        String sql = "insert into customer (id, firstname, lastname, street, city) values ("
                   + customer.getId() + ", "
                   + "'" + customer.getFirstName() + "', "
                   + "'" + customer.getLastName() + "', "
                   + "'" + customer.getStreet() + "', "
                   + "'" + customer.getCity() + "'"
                   + ")";

        Statement stmt = connection.createStatement();
        stmt.execute(sql);
        if (stmt.getUpdateCount() != 1) {
            throw new SQLException("Insert failed!");
        }
    }

}

Customer.java

/**
 * This is an example only! Use it for anything else at your own risk!
 * You have been warned! Coder/user beware!
 *
 * copyright 2009 Carlos Valcarcel
 */
package hiddenclause.example.dbunit;

/**
 * @author carlos
 *
 */
public class Customer {

    private int _id;
    private String _firstName;
    private String _lastName;
    private String _street;
    private String _city;

    public Customer(int id, String firstName, String lastName) {
        _id = id;
        _firstName = firstName;
        _lastName = lastName;
    }

    public int getId() {
        return _id;
    }

    public String getFirstName() {
        return _firstName;
    }

    public String getLastName() {
        return _lastName;
    }

    public String getStreet() {
        return _street;
    }

    public String getCity() {
        return _city;
    }

    public void setStreet(String street) {
        _street = street;
    }

    public void setCity(String city) {
        _city = city;
    }

}