Unit Test Support
Apache Causeway provides a number of helpers to help unit test your domain objects.
Maven Configuration
Dependency Management
If your application inherits from the Apache Causeway starter app (org.apache.causeway.app:causeway-app-starter-parent
) then that will define the version automatically:
<parent>
<groupId>org.apache.causeway.app</groupId>
<artifactId>causeway-app-starter-parent</artifactId>
<version>3.1.0</version>
<relativePath/>
</parent>
Alternatively, import the core BOM. This is usually done in the top-level parent pom of your application:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.causeway.core</groupId>
<artifactId>causeway-core</artifactId>
<version>3.1.0</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
In addition, add an entry for the BOM of all the testing support libraries:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.causeway.testing</groupId>
<artifactId>causeway-testing</artifactId>
<scope>import</scope>
<type>pom</type>
<version>2.1.0</version>
</dependency>
</dependencies>
</dependencyManagement>
Dependencies
In the domain module(s) of your application, add the following dependency:
<dependencies>
<dependency>
<groupId>org.apache.causeway.testing</groupId>
<artifactId>causeway-testing-unittestsupport-applib</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
We also highly recommend using the Fakedata library for random values in your tests:
<dependencies>
<dependency>
<groupId>org.apache.causeway.testing</groupId>
<artifactId>causeway-testing-fakedata-applib</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
The Parent POM configures the Maven surefire plugin in three separate executions for unit tests, integ tests and for BDD specs. This relies on a naming convention:
Classes named Not following this convention (intermixing unit tests with integ tests) may cause the latter to fail. |
Update AppManifest
In your application’s AppManifest
(top-level Spring @Configuration
used to bootstrap the app), import the CausewayModuleTestingUnitTestSupportApplib
module:
@Configuration
@Import({
...
CausewayModuleTestingUnitTestSupportApplib.class,
...
})
public class AppManifest {
}
(In fact, this is optional; the classes in the unit test support library can be used outside of a running application).
PojoTester
The PojoTester utility automates the testing of getters and setters.
As getters and setters are so simple, the intention of automating their testing is not to discover defects (though if there are unintentional side-effects, then these will be found). Instead, the rationale of testing getters and setters is to increase code coverage. Any substantial gap away from 100% would therefore due to significant functionality not having tests (as opposed to merely getters and setters not being tested).
The class has built-in support for primitives and most of the other value types already recognised by the framework, and provides a mechanism to allow example instances for any application-specific types.
For example, given a class:
@Getter @Setter
public class Customer {
private int custNum;
private String name;
private Date registeredOn;
private Address address;
private List<Order> orders;
}
then all of the getters and setters can be exercised using:
public class Customer_Test {
@Test
void exercise_getters_and_setters() {
PojoTester.create()
.usingData(Address.class, Address.class) (1)
.usingData(Order.class, Order.class) (1)
.exercise();
}
}
1 | provides the compile-time and run-time types of the referenced objects.
The runtime-type must provide a no-arg constructor.
In many cases (as here) the same type can be used for both parameters. |
There are several overloads of the usingData(…)
method, one of which accepts a PojoTester.DatumFactory
instance.
This interface provides a more flexible way to provide arbitrary datum instances for a specified type.
Contract Tests
Contract tests ensure that all instances of a particular idiom/pattern that occur within your codebase are implemented correctly. You could think of them as being a way to enforce a certain type of coding standard.
SortedSet
s
This contract test automatically checks that all fields of type java.util.Collection
are declared as java.util.SortedSet
.
In other words, it precludes either java.util.List
or java.util.Set
from being used as a collection.
For example, the following passes the contract test:
public class Department {
private SortedSet<Employee> employees = new TreeSet<Employee>();
...
}
whereas this would not:
public class SomeDomainObject {
private List<Employee> employees = new ArrayList<Employee>();
...
}
To use the contract test, subclass SortedSetsContractTestAbstract
, specifying the root package to search for domain classes.
For example:
public class SortedSetsContractTestAll extends SortedSetsContractTestAbstract {
public SortedSetsContractTestAll() {
super("com.mycompany.myapp.modules");
withLoggingTo(System.out);
}
}
If using an RDBMS for persistence then we strongly recommend that you implement this test, for several reasons:
-
first,
Set
s align more closely to the relational model than doList
s. AList
must have an additional index to specify order. -
second,
SortedSet
is preferable toSet
because then the order is well-defined and predictable (to an end user, to the programmer).The ObjectContracts utility class substantially simplifies the task of implementing
Comparable
in your domain classes. -
third, if the relationship is bidirectional then the ORM (JDO/DataNucleus) will automatically maintain the relationship.
Comparable
The ComparableContractTest_compareTo tests that a custom value type implements comparable()
correctly.
For example,
public class BigIntegerComparableContractTest_compareTo extends ComparableContractTest_compareTo<BigInteger> {
/**
* <code>
* item0 < item1 = item2 < item3
* </code>
*/
@Override
protected List<List<BigInteger>> orderedTuples() {
return listOf( (1)
listOf( (2)
BigInteger.ZERO,
BigInteger.ONE,
BigInteger.valueOf(1L),
BigInteger.TWO
),
listOf( (2)
BigInteger.valueOf(Long.MIN_VALUE),
BigInteger.valueOf(Long.MAX_VALUE / 2),
BigInteger.valueOf(Long.MAX_VALUE / 2),
BigInteger.valueOf(Long.MAX_VALUE)
)
);
}
}
1 | can return as many sets of candidate tuples as you wish |
2 | there must be four items in each tuple and they must satisfy: item0 < item1 == item2 < item3 . |
Value Objects
The ValueTypeContractTestAbstract tests that a custom value type implements equals()
and hashCode()
correctly.
For example, testing JDK’s own java.math.BigInteger
can be done as follows:
public class ValueTypeContractTestAbstract_BigIntegerTest extends ValueTypeContractTestAbstract<BigInteger> {
@Override
protected List<BigInteger> getObjectsWithSameValue() { (1)
return Arrays.asList(new BigInteger("1"), new BigInteger("1"));
}
@Override
protected List<BigInteger> getObjectsWithDifferentValue() { (2)
return Arrays.asList(new BigInteger("2"));
}
}
1 | Returns a list of different instances that are expected to have the same value (according to equals and hashCode). |
2 | Returns a list of instances that are different from each other and also from the instance(s) provided in the getObjectsWithSameValue() method.
As here, it is often sufficient to provide a single instance (as it is compared against the instances provided by getObjectsWithSameValue() . |
XML Marshalling Support
Apache Causeway' unit testing support provides the JaxbMatchers while the core applib also provides JaxbService.Simple service implementation.
These can be useful for example if you have example XML-serialized representations of the SOAP requests and response payloads and want to use these within your tests.
JMock Extensions
The JMock extensions are deprecated; we suggest you use Mockito instead. |
If you use mocking, then usual given/when/then format gets an extra step:
-
given the system is in this state
-
expecting these interactions (set up the mock expectations here)
-
when I poke it with a stick
-
then these state changes and interactions with Mocks should have occurred.
If using JMock then the interactions (in the "then") are checked automatically by a JUnit rule. However, you probably will still have some state changes to assert upon.
Apache Causeway' unit test support provides JUnitRuleMockery2
which is an extension to the JMock's JunitRuleMockery
.
It provides a simpler API and also providing support for autowiring.
For example, here we see that the class under test, an instance of CollaboratingUsingSetterInjection
, is automatically wired up with its Collaborator
:
public class JUnitRuleMockery2Test_autoWiring_setterInjection_happyCase {
@Rule
public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
@Mock
private Collaborator collaborator;
@ClassUnderTest
private CollaboratingUsingSetterInjection collaborating;
@Test
public void wiring() {
assertThat(collaborating.collaborator, is(not(nullValue())));
}
}
Distinguish between queries vs mutators
For mock interactions that simply retrieve some data, your test should not need to verify that it occurred.
If the system were to be refactored and starts caching some data, you don’t really want your tests to start breaking because they are no longer performing a query that once they did.
If using JMock API this means using the On the other hand mocks that mutate the state of the system you probably should assert have occurred.
If using JMock this typically means using the For more tips on using JMock and mocking in general, check out the GOOS book, written by JMock’s authors, Steve Freeman and Nat Pryce and also Nat’s blog. |