org.fuin:units4j

A home for additional useful unit testing modules for Java.


License
Other

Documentation

units4j

A home for additional useful unit testing modules for Java.

Java Maven Build Coverage Status Maven Central Javadocs LGPLv3 License Java Development Kit 17

Versions

  • 0.11.0 (or later) = Java 17 with junit5 / WeldJUnit4Runner removed in favor of weld-junit5
  • 0.10.0 = Java 11 with new jakarta namespace
  • 0.9.x = Java 11 before namespace change from 'javax' to 'jakarta'
  • 0.8.4 (or previous) = Java 8

Features


Asserting test coverage

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"));
}

Asserting package dependencies

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"));
}

Asserting methods are not used

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);

Assert that JPA entities are valid

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();

Assert that methods have information if null is allowed or not

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 at least one of the following annotations:

The package and case of those annotations does actually not matter as only simple name is checked. It is also possible to pass your own list of expected annotation simple names to the hasNullabilityInfoOnAllMethods() method.

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();

Assert that fields with @JsonbProperty annotation are not final

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

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>