Browse DevX
Sign up for e-mail newsletters from DevX


Efficient Test-Driven Design with Unitils : Page 2

Unitils is an open source testing framework for Java that dramatically reduces boilerplate test code. Test-driven design is a test-first methodology that uses programmatic tests as design tools. Find out how to realize the benefits of test-driven design using Unitils.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Getting Specific
The dataset you created represents a scenario. Now specify how you want EmployeeDaos to behave in this scenario with the descriptively named Unitils test findsForTitleOnly:

private IEmployeeDao employeeDao; @Test @DataSet public void findsForTitleOnly() throws DataAccessException { List<Integer> expectedEmployeeIds = Arrays.asList ( new Integer[] { 100, 103 } ); List<Employee> actualEmployees = employeeDao.findEmployeesByTitle("Software Engineer"); ReflectionAssert.assertPropertyRefEquals("id", expectedEmployeeIds, actualEmployees, ReflectionComparatorMode.LENIENT_ORDER); }

The @DataSet annotation is part of the Unitils DBUnit integration. It instructs the framework to automatically load your sample dataset into the database before running the test. The ReflectionAssert method verifies that the only Employee instances returned are those with the expected IDs. These special equality assertions included in Unitils provide a generic way to compare entire graphs of objects in a single line of code, which is a powerful way to express expectations.

The second method in the DAO, saveEmployees, implies a pretty general operation, but you're focused only on persisting salaries at the moment. Thankfully, the test-driven approach helps you stay focused only on specifying and implementing the behavior relevant to the use case at hand. So define a behavior named savesSalaryChanges, which is all you're really after for now, and then create a Unitils test.

Start with the opening scenario defined by the dataset in EmployeeDaoTest.savesSalaryChanges.xml. Because Unitils cleans the entire test database between test methods, these new records won't collide with the ones from your first test. (See Sidebar 2. Blech! All This XML by Hand?)

<dataset> <employees id="100" salary="54200.24" title="Worker Bee"/> <employees salary="70444.88"/> <employees salary="30200.55"/> <employees id="103" salary="40777.11" title="Worker Bee"/> <employees salary="82100"/> </dataset>

Now implement the test code itself, which gives all Worker Bees a nice raise:

@Test @DataSet @ExpectedDataSet public void savesSalaryChanges() throws DataAccessException { List<Employee> raiseEmployees = employeeDao.findEmployeesByTitle("Worker Bee"); raiseEmployees.get(0).setSalary(new BigDecimal(58000.00)); raiseEmployees.get(1).setSalary(new BigDecimal(61000.00)); employeeDao.saveEmployees(raiseEmployees); }

Notice the @ExpectedDataSet annotation. It is similar to @DataSet, except that it verifies a data state instead of establishing one, and it extends the naming convention for dataset files. After you call saveEmployees on your modified Worker Bees, you expect the dataset to look like that contained in EmployeeDaoTest.savesSalaryChanges-result.xml, which is what the @ExpectedDataSet annotation confirms for you. Here are the contents of that file:

<dataset> <employees id="100" salary="58000" title="Worker Bee"/> <employees salary="70444.88"/> <employees salary="30200.55"/> <employees id="103" salary="61000" title="Worker Bee"/> <employees salary="82100"/> </dataset>

Great! Now you have an executable specification for any class that claims to implement IEmployeeDao. Before you run it, you have to get the Unitils machinery going, which is thankfully as simple as setting up the unitils.properties file with a handful of fairly self-explanatory configuration properties. Once you've done that, you can invoke the test with your IDE's integrated JUnit test runner, or simply run "mvn test" at the console.

Now comes implementation, which is a radically efficient process under a test-driven methodology. Your mission is to get your tests to pass, which is a very linear and focused task because of how much feedback they give you. Starting with the initial "null pointer exception" from not assigning an implementation instance to employeeDao, do the minimum coding possible to fix the current error in your test before running it again. In most GUI test runners the famous "green bar" will be the indicator that you're finished—you've written just enough code to get the functionality you need and not a line more. Included in the project example is an implementation of IEmployeeDao using Ebean.

Thanks for your registration, follow us on our social networks to keep up-to-date