openxava / documentation / Lesson 3: Automated testing

Course: 1. Getting started | 2. Modeling with Java | 3. Automated testing | 4. Inheritance | 5. Basic business logic | 6. Advanced validation | 7. Refining the standard behavior | 8. Behavior & business logic | 9. References & collections | A. Architecture & philosophy | B. Java Persistence API | C. Annotations

Table of contents

Lesson 3: Automated testing
ModuleTestBase for testing the modules
The code for the test
Executing the tests from OpenXava Studio
Creating test data using JPA
Using setUp() and tearDown() methods
Creating data with JPA
Removing data with JPA
Filtering data from list mode in a test
Using entity instances inside a test
Using existing data for testing
Testing collections
Breaking down tests in several methods
Asserting default values
Data entry
Verifying the data
Good testing is the most important part of software development. It does not matter how beautiful, or fast, or high-tech your application is, if it crashes, you leave a poor impression and, crucially, your customer can lose valuable data.
Manual testing of the application as an end user is not a viable way to test, because the real problem is not the code you have written, but the existing code. Usually you test the code you have just written, but you do not retest all the already existing code in your application. And you know that when you touch any part of the code you can break any other part of the application.
While making any changes to your code, you want to handle it with the immense responsibility to keep the application from breaking. The best way to accomplish this is by using automatic testing. We are going to use automatic testing by means of JUnit.


JUnit is a popular tool for doing automated testing. This tool comes integrated with OpenXava Studio, so you do not need to download it. OpenXava extends the capacities of JUnit, allowing you to test an OpenXava module exactly in the same way an end user would. In fact, OpenXava uses HtmlUnit, software that simulates a real browser (including JavaScript) from Java. All this is available from the OpenXava class ModuleTestBase. It allows you to automate the test you would do by hand using a real browser in a simple way.
The best way to understand testing in OpenXava is to see it in action.

ModuleTestBase for testing the modules

The way to create a test for an OpenXava module is by extending the ModuleTestBase class from org.openxava.tests package. It connects to the OpenXava module as a real browser, and has a lot of methods that allows you to test your module. Let's create the test for your Customer module.

The code for the test

Create a new package named com.yourcompany.invoicing.tests and then create a new class named CustomerTest inside it with the next code:
package com.yourcompany.invoicing.tests;
import org.openxava.tests.*;
public class CustomerTest extends ModuleTestBase { // Must extend from ModuleTestBase
    public CustomerTest(String testName) {
        super(testName, "Invoicing", // We indicate the application name (Invoicing)
                "Customer"); // and the module name (Customer)
    // The test methods must start with 'test'
    public void testCreateReadUpdateDelete() throws Exception {
        login("admin", "admin"); // The user sign in to access the module
        // Create
        execute(""); // Clicks on 'New' button
        setValue("number", "77"); // Types 77 as the value for the 'number' field
        setValue("name", "JUNIT Customer"); // Sets the value for the 'name' field
        setValue("address.street", "JUNIT Street"); // Note the dot notation
                                                // to access a reference member
        setValue("address.zipCode", "77555"); // Etc
        setValue("", "The JUNIT city"); // Etc
        setValue("address.state", "The JUNIT state"); // Etc
        execute(""); // Clicks on 'Save' button
        assertNoErrors(); // Verifies that the application does not show errors
        assertValue("number", ""); // Verifies the 'number' field is empty
        assertValue("name", ""); // Verifies the 'name' field is empty
        assertValue("address.street", ""); // Etc
        assertValue("address.zipCode", ""); // Etc
        assertValue("", ""); // Etc
        assertValue("address.state", ""); // Etc
        // Read
        setValue("number", "77"); // Types 77 as the value for the 'number' field
        execute("CRUD.refresh"); // Clicks on 'Refresh' button
        assertValue("number", "77"); // Verifies the 'number' field has 77
        assertValue("name", "JUNIT Customer"); // and 'name' has 'JUNIT Customer'
        assertValue("address.street", "JUNIT Street"); // Etc
        assertValue("address.zipCode", "77555"); // Etc
        assertValue("", "The JUNIT city"); // Etc
        assertValue("address.state", "The JUNIT state"); // Etc
        // Update
        setValue("name", "JUNIT Customer MODIFIED"); // Changes the value
                                                    // of 'name' field
        execute(""); // Clicks on 'Save' button
        assertNoErrors(); // Verifies that the application does not show errors
        assertValue("number", ""); // Verifies the 'number' field is empty
        assertValue("name", ""); // Verifies the 'name' field is empty
        // Verify if modified
        setValue("number", "77"); // Types 77 as the value for 'number' field
        execute("CRUD.refresh"); // Clicks on 'Refresh' button
        assertValue("number", "77"); // Verifies the 'number' field has 77
        assertValue("name", "JUNIT Customer MODIFIED"); // and 'name'
                                        // has 'JUNIT Customer MODIFIED'
        // Delete
        execute("CRUD.delete"); // Clicks on 'Delete' button
        assertMessage("Customer deleted successfully"); // Verifies that the
                // message 'Customer deleted successfully' is shown to the user
This test creates a new customer, searches it, modifies it, and finally deletes it. You see how you can use methods such as execute() or setValue() for simulating user actions, and the methods like assertValue(), assertNoErrors() or assertMessage() to verify the state of the user interface. Your test acts as the hands and eyes of the user:
In execute() you must specify the qualified name of the action, that means ControllerName.actionName. How can you know it? Simple, put your mouse over an action link, and you will see in the status bar of your browser a JavaScript code that includes the qualified action name:
Now, you know how to create a test for testing the basic CRUD operations of a module. It's not required to write an exhaustive test at first. Just test the basic things, those that you usually test using the browser. Your test will naturally grows with your application and as the user feedback grows.
Let's learn how to execute your test from OpenXava Studio.

Executing the tests from OpenXava Studio

As mentioned earlier, JUnit is integrated into OpenXava Studio, so running your tests is plain vanilla. In order your test works your application must be running, if not start it first. Then run the test with the mouse over the test class, and with the right button choose Run As > JUnit Test:
If the test does not pass, the bar will be red. You can try it. Edit the CustomerTest and comment the line that sets the value for the name field:
setValue("number", "77");
// setValue("name", "JUNIT Customer"); // Comment this line
setValue("address.street", "JUNIT Street");
Now, rerun the test. Since name is a required property, an error message will be shown to the user, and the object will not be saved:
The failed assert is assertNoErrors(), which in addition to failure shows the error on the console. So, in the execution console of your test you will see the message like this one:
16-jul-2019 18:03 org.openxava.tests.ModuleTestBase assertNoMessages
SEVERE: Error unexpected: Value for Name in Customer is required
The problem is clear. The customer is not saved because the name is required, and it's not specified.
You have seen how the test behaves when it fails. Now, you can uncomment back the guilty line and run the test again to verify that everything is OK.

Creating test data using JPA

In your first test, CustomerTest, the test itself starts creating the data that is used in the rest of the test. This is a good way to go, especially if you want to test the data entry functionality too. But, if you want to test only a small buggy case, or your module simply does not allow adding new objects, you can create the data you need for testing using JPA from your test.

Using setUp() and tearDown() methods

We are going to use ProductTest to learn how to use JPA for creating test data. We'll create some products before each test execution and remove them afterwards. Let's see the code for ProductTest:
package com.yourcompany.invoicing.tests;
import java.math.*;
import com.yourcompany.invoicing.model.*;
import org.openxava.tests.*;
import static org.openxava.jpa.XPersistence.*;
public class ProductTest extends ModuleTestBase {
    private Author author; // We declare the entities to be created
    private Category category; // as instance members in order
    private Product product1; // to be available from inside any test method
    private Product product2; // and to be removed at the end of each test
    public ProductTest(String testName) {
        super(testName, "Invoicing", "Product");
    protected void setUp() throws Exception { // setUp() is always executed
                                              // before each test
        super.setUp(); // It's needed because ModuleTestBase uses it for
                        // initializing, even JPA is initialized here.
        createProducts(); // Creates the data used in the tests
    protected void tearDown() throws Exception { // tearDown() is always
                                                 // executed after each test
        super.tearDown(); // It's needed, ModuleTestBase closes resources here
        removeProducts(); // The data used for testing is removed
    public void testRemoveFromList() throws Exception { ... }
    public void testUploadPhotos() throws Exception { ... }
    private void createProducts() { ... }
    private void removeProducts() { ... }
Here we are overwriting the setUp() and tearDown() methods. These methods are JUnit methods that are executed just before and after each test execution. We create the testing data before each test execution, and remove the data after each test execution. Thus, each test can rely on the precise data to be executed. It does not matter if some other test removes or modifies the data, or the execution order of the test. Always, at the beginning of each test we have all the data ready to use.

Creating data with JPA

The createProducts() method is responsible for creating the test data using JPA. Let's examine it:
private void createProducts() {
    // Creating the Java objects
    author = new Author(); // Regular Java objects are created
    author.setName("JUNIT Author"); // We use setters just as in plain Java
    category = new Category();
    category.setDescription("JUNIT Category");
    product1 = new Product();
    product1.setDescription("JUNIT Product 1");
    product1.setPrice(new BigDecimal("10"));
    product2 = new Product();
    product2.setDescription("JUNIT Product 2");
    product2.setPrice(new BigDecimal("20"));
    // Marking as persistent objects
    getManager().persist(author); // getManager() is from XPersistence
    getManager().persist(category); // persist() marks the object as persistent
    getManager().persist(product1); // so it will be saved to the database
    // Commit changes to the database
    commit(); // commit() is from XPersistence. It saves all object to the database
              // and commits the transaction
As you can see, first you create the Java objects in the Java conventional way. Note that you assign it to instance members. Thus you can use it inside tests. Then, you mark them as persistent, using the persist() method of the JPA EntityManager. To obtain the EntityManager you only have to write getManager() because you have the static import above:
import static org.openxava.jpa.XPersistence.*;
    // Thanks to the XPersistence static import it's the same as
    // Thanks to the XPersistence static import it's the same as
To finalize, commit() (also from XPersistence) saves all the data from objects to database and then commits the transaction. After that, the data is in the database ready to be used by your test.

Removing data with JPA

After the test is executed we remove the test data in order to leave the database clean. This is done by the removeProducts() method:
private void removeProducts() { // Called from tearDown() so it's executed
                                // after each test
    remove(product1, product2, author, category); // remove() removes
    commit(); // Commits the changes to database, in this case deleting data
private void remove(Object ... entities) { // Using varargs argument
    for (Object entity : entities) { // Iterating for all arguments
        getManager().remove(getManager().merge(entity)); // Removing(1)
It's a simple loop to remove all the entities used in the test. To remove an entity in JPA you have to use the remove() method, but in this case you have to use the merge() method too (shown as 1). This is because you cannot remove a detached entity. When you use commit() in createProducts() all saved entities become detached entities. This is because they continue being valid Java object but the persistent context (the union between entities and database) has been lost on commit(), so you must reattach them to the new persistent context. This concept is easy to understand seeing the next code:
getManager().persist(author); // author is attached to the current persistence context
commit(); // The current persistence context is over, so author becomes detached
getManager().remove(author); // It fails because author is detached
author = getManager().merge(author); // Reattaches author to the current context
getManager().remove(author); // It works
Apart from this curious detail about merge(), the code for removing is quite simple.

Filtering data from list mode in a test

Now that you know how to create and remove the data for the tests, let's examine the test methods for your Product module. The first one is testRemoveFromList() that checks a row in list mode and clicks on Delete selected button. Let's see the code:
public void testRemoveFromList() throws Exception {
    login("admin", "admin");
    setConditionValues("", "JUNIT"); // Put the values for filtering data
    setConditionComparators("=", "contains_comparator"); // Put the comparators for filtering data
    execute("List.filter"); // Clicks on filter button
    assertListRowCount(2); // Verifies that there are 2 rows
    checkRow(1); // We select row 1 (really the second one)
    execute("CRUD.deleteSelected"); // Clicks on the delete button
    assertListRowCount(1); // Verifies that now there is only 1 row
Here we filter in list mode all products that contain the “JUNIT” word (remember that you have created two of them in createProducts() method), then we verify that there are two rows, select the second product, and remove it, verifying at the end that one product remains in the list.
You have learned how to select a row (using checkRow()) and how to assert the row count (using assertListRowCount()). The trickiest part might be filtering the list using setConditionValues() and setConditionComparators(). Both methods receive a variable number of string arguments with values and comparators for the condition, just as shown here:
The values are assigned to the list filter user interface sequentially (from left to right). In this case there are two values, but you can use all you need. You do not need to specify all values. The setConditionValues() method accepts any string value whereas setConditionComparators() accepts the next possible values: contains_comparator, starts_comparator, ends_comparator, not_contains_comparator, empty_comparator, not_empty_comparator, =, <>, >=, <=, >, <, in_comparator, not_in_comparator and range_comparator.

Using entity instances inside a test

The remaining test, testUploadPhotos(), chooses a product and upload photos on it. We are going to use in the test an entity created in createProducts():
public void testUploadPhotos() throws Exception { 
	login("admin", "admin");
	// Searching the product1
	setValue("number", Integer.toString(product1.getNumber())); // (1)
	assertFilesCount("photos", 0);
	// Uploading photos
	uploadFile("photos", "web/xava/images/add.gif"); // (2)
	uploadFile("photos", "web/xava/images/attach.gif"); // (2)
	// Verifying
	assertFilesCount("photos", 0);
	setValue("number", Integer.toString(product1.getNumber())); // (1)
	assertFilesCount("photos", 2);
	assertFile("photos", 0, "image");
	assertFile("photos", 1, "image");
	// Remove photos
	removeFile("photos", 1);
	removeFile("photos", 0);
The interesting thing in this test is to provide a value to number to search the product. We get the value using product1.getNumber() (shown as 1). Remember that product1 is an instance variable of the test that is populated in createProducts(), which is called from setUp() so it is executed before each test. Also you have learned how to use the methods uploadFile(), assertFileCount(), assertFile() and removeFile() to work with photos. These methods work with any property that uploads files (IMAGES_GALLERY, PHOTO, IMAGE, FILE, FILES, etc). In this case we use images included in OpenXava, gifs from web/xava/images (shown as 2), but you can create your own folder with your own images for testing.

You have a test class for Product and at the same time you have learned testing with test data created using JPA. Run it, it should be green.

Using existing data for testing

Sometimes you can simplify the test by relying on a test database which is populated with the data needed for testing. If you do not want to test data creation from the module itself, and you do not remove data in the test, this can be a good option.
For example, you can test Author and Category with a simple test like this one:
package com.yourcompany.invoicing.tests;
import org.openxava.tests.*;
public class AuthorTest extends ModuleTestBase {
    public AuthorTest(String testName) {
        super(testName, "Invoicing", "Author");
    public void testReadAuthor() throws Exception {
        login("admin", "admin");
        assertValueInList(0, 0, "JAVIER CORCOBADO"); // The first author
                                        // in the list is JAVIER CORCOBADO
        execute("List.viewDetail", "row=0"); // We click the first object in the list
        assertValue("name", "JAVIER CORCOBADO");
        assertCollectionRowCount("products", 2); // It has 2 products
        assertValueInCollection("products", 0, // Row 0 of products
                                "number", "2"); // has “2” in “number” column
        assertValueInCollection("products", 0, "description", "Arco iris de lágrimas");
        assertValueInCollection("products", 1, "number", "3");
        assertValueInCollection("products", 1, "description", "Ritmo de sangre");
This test verifies that the first author in the list is “JAVIER CORCOBADO”, remember to create it before executing the test. It goes to the detail and asserts that it has a collection called "products" with 2 products: “Arco iris de lágrimas” and “Ritmo de sangre”, before executing the test create them and associate them to "JAVIER CORCOBADO". By the way, now you have learned how to use assertValueInList(), assertValueInCollection() and assertCollectionRowCount() methods.
We can use the same technique to test the Category module:
package com.yourcompany.invoicing.tests;
import org.openxava.tests.*;
public class CategoryTest extends ModuleTestBase {
    public CategoryTest(String testName) {
        super(testName, "Invoicing", "Category");
    public void testCategoriesInList() throws Exception {
        login("admin", "admin");
        assertValueInList(0, 0, "MUSIC"); // Row 0 column 0 has “MUSIC”
        assertValueInList(1, 0, "BOOKS"); // Row 1 column 0 has “BOOKS”
        assertValueInList(2, 0, "SOFTWARE"); // Row 2 column 0 has “SOFTWARE”
In this case we see that in the list the first three categories are “MUSIC”, “BOOKS” and “SOFTWARE”. Remember to create them before executing the test.
You have seen how the technique of using pre-existing data from a test database allows you to create simpler tests. Starting from a simple test and further complicating it on demand is a good way to go.
Remember to add the corresponding data using the modules before executing these tests.

Testing collections

Now it's time to face the test for the main module of your application, the InvoiceTest. As of now the functionality of the Invoice module is limited. You can only add, remove and modify invoices. Even so, this is a big test. It contains a collection, so you will learn here how to test collections.

Breaking down tests in several methods

The test for creating an Invoice is broken down into several methods:
package com.yourcompany.invoicing.tests;
import java.time.*; import java.time.format.*;
import javax.persistence.*; import org.openxava.tests.*; import static org.openxava.jpa.XPersistence.*; // To use JPA public class InvoiceTest extends ModuleTestBase { private String number; // To store the number of the tested invoice public InvoiceTest(String testName) { super(testName, "Invoicing", "Invoice"); } public void testCreate() throws Exception { // The test method login("admin", "admin"); verifyDefaultValues(); chooseCustomer(); addDetails(); setOtherProperties(); save(); verifyCreated(); remove(); } private void verifyDefaultValues() throws Exception { … } private void chooseCustomer() throws Exception { … } private void addDetails() throws Exception { … } private void setOtherProperties() throws Exception { … } private void save() throws Exception { … } private void verifyCreated() throws Exception { … } private void remove() throws Exception { … } private String getCurrentYear() { … } private String getCurrentDate() { … } private String getNumber() { … } }
The only test method in this class is testCreate(), but because this test is somewhat large, it is better to break it down into several shorter methods. In fact, it's a good object-oriented practice to write short methods.
Because this method is short you can see in a glance what it does. In this case the method verifies the default values for a new invoice, chooses a customer, adds the details, adds other properties, saves the invoice, verifies that it is correctly saved and finally deletes it. Let's dip into the details of these steps.

Asserting default values

First, it verifies whether the default values for a new invoice are correctly calculated or not. This is done by the verifyDefaultValues() method:
private void verifyDefaultValues() throws Exception {
    assertValue("year", getCurrentYear());
    assertValue("number", getNumber());
    assertValue("date", getCurrentDate());
When the user clicks on New, the year, number and date field must be prefilled with valid data. The verifyDefaultValues() method tests this. It uses several utility methods to calculate the expected values:
private String getCurrentYear() { // Current year in string format
    return Integer.toString(; // The standard
                                                    // way to do it with Java
private String getCurrentDate() { // Current date as string in short format
    return // The standard way to do it with Java
private String getNumber() { // The invoice number for a new invoice
    if (number == null) { // We use lazy initialization
        Query query = getManager(). // A JPA query to get the last number
                createQuery("select max(i.number) from Invoice i where i.year = :year");
        Integer lastNumber = (Integer) query.getSingleResult();
        if (lastNumber == null) lastNumber = 0;
        number = Integer.toString(lastNumber + 1); // Adding 1 to the last invoice number
    return number;
The getCurrentYear() and getCurrentDate() methods use classic Java techniques to format the date as string.
The getNumber() method, on the other hand, is a little more complex. It uses JPA to calculate the last invoice number of the current year, then return this value plus one. Due to its access to the database it is heavier than a simple Java calculation, therefore we use lazy initialization. Lazy initialization delays the calculation until the first time it is needed, and stores it for future use. We do it by saving the value in the number field.

Data entry

Now it's time for the chooseCustomer() method of the invoice:
private void chooseCustomer() throws Exception {
    setValue("customer.number", "1");
    assertValue("", "JAVIER PANIZA"); // The customer 1 should exist in DB
Upon entry of the customer number the customer name is simply filled with the appropriate value. Since the test relies on customer 1 with name "JAVIER PANIZA" existing already, you should create it before running the test. With this the customer 1 is associated to the current invoice.
And now comes the most tricky part of the test: adding the detail lines:
private void addDetails() throws Exception {
    assertCollectionRowCount("details", 0); // The collection is empty
    // Adding a detail line
    setValueInCollection("details", 0, // 0 means the first row
        "product.number", "1");
    assertValueInCollection("details", 0,
        "product.description", "Peopleware: Productive Projects and Teams");
    setValueInCollection("details", 0, "quantity", "2");
    // Adding another detail
    setValueInCollection("details", 1, "product.number", "2");
    assertValueInCollection("details", 1, "product.description", "Arco iris de lágrimas");
    setValueInCollection("details", 1, "quantity", "1");
    assertCollectionRowCount("details", 2); // Now we have 2 rows
Testing a collection is the same as testing any other part of your application. You have to follow the same steps as an end user with a browser. You have methods such as setValueInCollection(), assertValueInCollection() or assertCollectionRowCount() to work with collections. Note how these methods have the collection name as first argument, and some of them receive the row number with 0 based index. Remember to create the product 1 and 2 with the corresponding descriptions before executing this test.
Now that we have the details added, we are going to fill the remaining data and save the invoice. The remaining data is set by setOtherProperties() method:
private void setOtherProperties() throws Exception {
    setValue("remarks", "This is a JUNIT test");
Here we give a value to the remarks field. Now we are ready to save the invoice:
private void save() throws Exception {
    assertValue("customer.number", "");
    assertCollectionRowCount("details", 0);
    assertValue("remarks", "");
It simply clicks on Save, then verifies for any errors and makes sure that the view is clean.

Verifying the data

Now, we will search the newly created invoice to verify that it has been saved correctly. This is done by the verifyCreated() method:
private void verifyCreated() throws Exception {
    setValue("year", getCurrentYear()); // The current year to year field
    setValue("number", getNumber()); // The invoice number of the test
    execute("CRUD.refresh"); // Load the invoice back from the database
    // In the rest of the test we assert that the values are the correct ones
    assertValue("year", getCurrentYear());
    assertValue("number", getNumber());
    assertValue("date", getCurrentDate());
    assertValue("customer.number", "1");
    assertValue("", "JAVIER PANIZA");
    assertCollectionRowCount("details", 2);
    // Row 0
    assertValueInCollection("details", 0, "product.number", "1");
    assertValueInCollection("details", 0, "product.description",
        "Peopleware: Productive Projects and Teams");
    assertValueInCollection("details", 0, "quantity", "2");
    // Row 1
    assertValueInCollection("details", 1, "product.number", "2");
    assertValueInCollection("details", 1, "product.description",
        "Arco iris de lágrimas");
    assertValueInCollection("details", 1, "quantity", "1");
    assertValue("remarks", "This is a JUNIT test");
After searching the created invoice we verify whether the values we have saved are there. If the test reaches this point your Invoice module works fine. The only thing remaining is to delete the created invoice so that the test can be executed again. We do that in the remove() method:
private void remove() throws Exception {
It just clicks on Delete and verifies that no errors are produced.

Congratulations! You have your InvoiceTest completed. Execute it, it should be green; if not revise the data in the database, maybe you have to add the corresponding products, customer, etc.


You have 5 test cases to preserve the quality of your application. When you finish some enhancement or fix of your application you must execute all your tests to ensure that your already existing functionality is not broken.
Traditionally, to execute all the test for your application you have to create a test suite, and execute it. A test suite is a class that aggregates all your JUnit tests so you can execute them all at once. Fortunately, with OpenXava Studio you do not need to write a test suite class, it allows you to execute all the tests for your application automatically:
That is, if you execute Run As > JUnit Test on the project then all its JUnit tests are executed.


You have automated the tests for all the current functionality of your application. This test code seems to be more verbose and boring than the real application code. But remember, the test code is the most valuable asset you have. Right now you may not believe me, but try to do tests and once they save your life, you will not develop without test code any more.
What to test? Don't do an exhaustive test at first. It's better to test a little than to test nothing. If you try to do exhaustive testing you will end up testing nothing. Start doing a little testing of all your code, and with any new feature or fix also write the test for it. In the end, you will have a very powerful test suite. Test little but test always.
In fact, testing is an on going task. In order to preach with the example, from now on we'll write all the tests for the code we will develop in the rest of the book. Thus you will learn more tips about testing in the next lessons.

Download source code of this lesson

Any problem with this lesson? Ask in the forum Everything fine? Go to Lesson 4