Expanders Assert
Expanders assert is a library to test Expanders and Features. It improves on the expanders-test-utils library to fix a few long-standing issues:
- The expansionResource manifest for the project is generated ad hoc, which makes the tests more predictable.
- The framework actively checks for classpath conflicts and gives clear error messages.
There are also improvements which make it easier to create tests with new Metamodels:
- Expanders-assert fixes issues with creating ExpansionContexts by using the same infrastructure that is used during expansion.
- Finding elements in your test models has been simplified by using the DataRegistry.
Setup
Add the following dependency to your expanders project:
<dependency>
<groupId>net.democritus.test</groupId>
<artifactId>expanders-assert</artifactId>
<version>1.1.0</version>
<scope>test</scope>
</dependency>
The library is based on junit 5 with AssertJ. Add these to the test dependencies with:
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.26.3</version>
<scope>test</scope>
</dependency>
</dependencies>
ExpanderTest
Each Expanders test should be annotated with @ExpanderTest
.
This will enable the junit 5 extensions to test the Expander.
import net.democritus.expander.junit.ExpanderTest;
@ExpanderTest
public class SampleExpanderTest {
}
By default, the test will look for an Expander with the same name as the test class minus the Test
-suffix.
If the Test class name does not match the Expander, you can provide the name of the Expander in the @ExpanderTest
annotation.
@ExpanderTest(name = "net.democritus.expander.SampleExpander")
FeatureTest
Feature tests (both for the feature itself and the ExpanderFeatures), should be annotated with @FeatureTest
.
import net.democritus.expander.junit.FeatureTest;
@FeatureTest
public class MyFeatureTest {
}
The Feature (and ExpanderFeature) are resolved similarly to the Expander, based on the Test class name:
- If the Test class matches the Feature name (e.g.
MyFeatureTest
forMyFeature
), then it will select that feature and pick the first of its ExpanderFeature. - If the Test class matches the ExpanderFeature name (e.g.
MyFeatureBeanExpanderTest
forMyFeature.BeanExpander
), then it will select that ExpanderFeature and its Feature.
It is also possible to explicitly define the Feature and Expander.
@Feature(feature = "net.democritus.feature.MyFeature")
@Feature(
feature = "net.democritus.feature.MyFeature",
expander="net.democritus.expander.ejb3.dataElement.BeanExpander")
Test Model
Every test starts with a test model.
To define this model, create a method names baseModel()
with the @TestModel
annotation.
import net.democritus.expander.junit.ExpanderTest;
@ExpanderTest
public class SampleExpanderTest {
@TestModel(select = "testComp::City")
Spec baseModel() {
return component("testComp",
dataElement("City"));
}
}
The model is defined using Onion Specs. You can optionally define the select parameter to select an Element within the model.
TestExpansion
Each test method receives a TestExpansion instance. This object allows you to assert conditions of your Expander/Feature.
Testing Expanders
To test the results of an Expander, use testExpansion.assertThatExpander()
.
@ExpanderTest
public class SampleExpanderTest {
@TestModel(select = "testComp::City")
Spec baseModel() {
return component("testComp",
dataElement("City",
set("packageName", "net.demo.cities")));
}
@Test
void test_artifactPath(TestExpansion<DataElementComposite> expansion) {
/*
Test the result of the artifact path definition in the Expander XML file.
`$componentRoot.directory$` or `module.rootDirectory` will be translated to `C:/MODULE_ROOT`.
`$applicationRoot.directory` or `program.rootDirectory` will be translated to `C:/PROGRAM_ROOT`.
*/
expansion
.assertThatExpander()
.hasArtifactPath("C:/MODULE_ROOT/net/demo/cities/City.java");
}
@Test
void test_isApplicable(TestExpansion<DataElementComposite> expansion) {
/* Test isApplicable condition defined in the Expander XML file */
expansion
/* extendModel() allows you to add additional specifications to the model */
.extendModel(option("example.option"))
.assertThatExpander().isApplicable();
}
@Test
void test_isNotApplicable(TestExpansion<DataElementComposite> expansion) {
expansion.assertThatExpander().isNotApplicable();
}
@Test
void test_base(TestExpansion<DataElementComposite> expansion) {
/*
Test the base content generated by the expander.
*/
expansion.assertThatExpander().baseContent().matchesTemplate();
}
@Test
void test_anchor_fields(TestExpansion<DataElementComposite> expansion) {
/*
Test the `fields` anchor.
*/
expansion
.extendModel(
field("name: String"),
field("status: String"))
.assertThatExpander().anchor("fields")
.matchesTemplate();
}
}
Base Content
expansion.assertThatExpander().baseContent()
allows you to match the content your Expander generates.
To make testing more manageable, it is possible to put anchors around parts of the expander template.
When testing the base content, the content between those anchors is filtered out.
base() ::= <<
package <packageName>;
// <expanderComment>
public class <dataElement> {
@anchor:fields
// anchor:fields:start
<fields:{f|private <f.type> <f.name>;};separator="\n">
// anchor:fields:end
}
>>
test_base() ::= <<
package net.demo.cities;
// @expanderComment@
public class City {
// anchor:fields:start
// anchor:fields:end
}
>>
matchesTemplate
will compare the content to a template in the ExpanderTest stg file with the same name as
this method (test_base
in this case).
Both the expander template and expander test template are evaluated by the template engine. Because the \
character is
an escape character for this engine, it is required to escape that character twice in both templates if it occurs in the
artifact that is being expanded.
Example: If an escaped backslash \\
is needed in a piece of expanded code because it occurs in a string, both the
expander template AND test template must double escape it as \\\\
.
Anchors
The content between anchors can be tested separately with expansion.assertThatExpander().anchor("fields")
, fields
here being the name of the anchor.
test_anchor_fields() ::= <<
private String name;
private String status;
>>
Test Features
To test Features, we use testModel.assertThatFeature()
.
@FeatureTest
public class MyFeatureTest {
@TestModel(select = "testComp::City")
Spec baseModel() {
return component("testComp",
dataElement("City"));
}
@Test
public void test_isApplicable(TestExpansion<DataElementComposite> expansion) {
/* Test condition in Feature XML. */
expansion
.extendModel(option("example.feature"))
.assertThatFeature().isApplicable();
}
@Test
public void test_isNotApplicable(TestExpansion<DataElementComposite> expansion) {
expansion.assertThatFeature().isNotApplicable();
}
}
@FeatureTest
public class SampleFeatureSampleExpanderTest {
@TestModel(select = "testComp::City")
Spec baseModel() {
return component("testComp",
dataElement("City"));
}
@Test
public void test_base(TestExpansion<DataElementComposite> expansion) {
/* Test the base content of the ExpanderFeature. */
expansion.assertThatFeature().baseContent().matchesTemplate();
}
@Test
public void testImports(TestExpansion<DataElementComposite> expansion) {
/* Test imports that were added with usage statements in the mapping file.*/
expansion.assertThatFeature().imports().matchesTemplate();
}
}