A home for additional useful unit testing modules for Java.
- Asserting test coverage
- Asserting package dependencies
- Asserting methods are not used
- Assert that JPA entities are valid
- Assert that methods have information if null is allowed or not
- Assert that fields with @JsonbProperty annotation are not final
A good approach is to have at least one test class for every production class.
If you have classes this rule-of-thumb does not apply, you can:
- Write a dummy test class with a comment that describes why not...
- Use an AssertCoverage.ClassFilter to exclude the classes
@Test
public void testCoverage() {
AssertCoverage.assertEveryClassHasATest(new File("src/main/java"));
}
It's a good practice enforcing package dependencies to avoid high coupling and package cycles.
You simply define a dependency description in your "src/test/resources" folder. For an example see units4j.xml).
@Test
public void testAssertDependencies() {
AssertDependencies.assertRules(getClass(), "/units4j.xml", new File("target/classes"));
}
Example: Prevent a java.lang.ArithmeticException Non-terminating decimal expansion; no exact representable decimal result." caused by calling BigDecimal's divide or setScale without a rounding mode:
// Path to '*.class' files
File classesDir = new File("target/classes");
// Can be used to exclude some files/packages
FileFilter fileFilter = new FileFilter() {
@Override
public boolean accept(File file) {
return !file.getPath().contains("my/pkg/to/exclude");
}
};
// Define methods to find
MCAMethod divide = new MCAMethod("java.math.BigDecimal", "java.math.BigDecimal divide(java.math.BigDecimal)");
MCAMethod setScale = new MCAMethod("java.math.BigDecimal","java.math.BigDecimal setScale(int)");
// Fails if any class calls one of the two methods
AssertUsage.assertMethodsNotUsed(classesDir, fileFilter, divide, setScale);
Uses JBoss Jandex to validate JPA entity classes.
import static org.fuin.units4j.JandexAssert.assertThat;
// Collect all class files
File dir = new File("target/classes");
List<File> classFiles = Units4JUtils.findAllClasses(dir);
Index index = Units4JUtils.indexAllClasses(classFiles);
// Verify that all classes annotated with @Entity or @MappedSuperclass observe
// the rules for JPA entities (Class not final + No final methods + ...).
assertThat(index).hasOnlyValidJpaEntities();
WORK IN PROGRESS See Issue #9
It's a good style to define a precondition for method arguments and postconditions for return values of externally used methods. Especially the questions "Can I pass null?" or "Does the method return null values?" is a common source of confusion. This assertion makes sure that all return values and parameters of all public, protected and package-private methods have either a @NotNull or a @Nullable annotation.
Example:
public interface MyInterface {
// Post condition says the return value is never null
@NotNull
public Boolean myMethodA();
// Pre condition says the first value cannot be null, but it's OK for the second argument
public void myMethodB(@NotNull Integer abc, @Nullable String def);
// Post condition says the return value may be null
@Nullable
public Long myMethodC();
}
Test:
import static org.fuin.units4j.JandexAssert.assertThat;
// Collect all class files
File dir = new File("target/classes");
List<File> classFiles = Units4JUtils.findAllClasses(dir);
Index index = Units4JUtils.indexAllClasses(classFiles);
// Verify that all methods make a statement if null is allowed or not
assertThat(index).hasNullabilityInfoOnAllMethods();
Verifies that no field that has a @JsonbProperty annotation. The deserialization using a Eclipse Yasson FieldAccessStrategy will fail otherwise silently.
Uses JBoss Jandex to validate JSON-B fields.
import static org.fuin.units4j.JandexAssert.assertThat;
// Collect all class files
File dir = new File("target/classes");
List<File> classFiles = Units4JUtils.findAllClasses(dir);
Index index = Units4JUtils.indexAllClasses(classFiles);
// Verify that no field that has a 'javax.json.bind.annotation.JsonbProperty' annotation.
// The deserialization using a 'org.eclipse.yasson.FieldAccessStrategy' will fail otherwise.
assertThat(index).hasNoFinalFieldsWithJsonbPropertyAnnotation();
Snapshots can be found on the OSS Sonatype Snapshots Repository.
Add the following to your .m2/settings.xml to enable snapshots in your Maven build:
<repository>
<id>sonatype.oss.snapshots</id>
<name>Sonatype OSS Snapshot Repository</name>
<url>http://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>