Defining Test Models with Onion Specs
Onion specs are an API to define test models in a compact way. They are layered, like onions, to construct a hierarchical model.
In this example, you can see how a Component with 3 DataElements is constructed by nesting the specification methods:
component("testComponent",
dataElement("City"),
dataElement("Country"),
dataElement("District"));
Each specification methods has a similar signature:
public static ComponentSpec component(String specification, Spec... children) {}
- The first argument,
specification
, describes the element itself. Usually, it is thename
of the element. - After this, you can add any number of child specifications, which are then applied to the parent.
Setting Attributes/References
To set an attribute other than name, we use CommonSpecs.set(attributeName, value)
.
E.g. to set the packageName of a DataElement:
import static net.democritus.spec.CommonSpecs.set;
//...
dataElement("City",
set("packageName", "net.demo.cities"))
References to other elements are defined with CommonSpecs.dataRef(referenceName, key)
.
E.g. to set the targetElement of a TaskElement:
import static net.democritus.spec.CommonSpecs.dataRef;
//...
component("testComp",
dataElement("City"),
taskElement("CityExporter",
dataRef("targetElement", "testComp::City")))
The notation used here to define the City
dataElement is a functional key.
It references the element by adding the name of the component.
In the same way, we can reference a field with testComp::City::postcode
Element Options
Options can be added to elements using option("name: value")
.
dataElement("City",
option("cruds.table.pageSize: 50"))
If the option does not require a value, use option("name")
instead.
DataElements, Fields and Finders
A dataElement can contain fields and finders. To reduce the complexity, fields and finders have a shorthand syntax:
dataElement("City",
field("postcode: String*"),
field("country: Country[Ln01]"),
field("/population: Long*"),
finder("findByPostcodeEq"))
In this example we have:
- A ValueField postcode with ValueType String
- A LinkField country with target element Country and LinkFieldType Ln01
- A CalculatedField (recognized by the
/
-prefix) population with ValueType Long - A Finder findByPostcodeEq with FieldOperatorPair postcode-eq, which is added based on the Finder's name
A ValueField can either be defined as field("name: valueType*")
or field("name: valueFieldType")
.
The first variation (with *
) will use the newer ValueType.
The second variation will use the outdated ValueFieldType.
DataProjections
To define a dataProjection, you can add referenceFields and calculatedFields. CalculatedFields will have a syntax similar to the value-fields. ReferenceFields should contain the name of the fields they are referencing.
dataElement("City",
field("postcode: String*"),
field("country: Country[Ln01]"),
dataProjection("AreaInfo",
referenceField("postcode"),
calculatedField("size: Long")))
DataCommands
DataCommands can have connectorFields which are defined the same as regular fields, except they cannot be calculated.
dataElement("City",
field("postcode: String*"),
field("country: Country[Ln01]"),
dataCommand("addDistrict",
set("hasTargetInstance", true),
connectorField("name: String*"),
connectorField("location: Location[Ln01]")))