Integration testing
GenesisJunit is only available from version 8 of the Genesis Server Framework (GSF). If you are testing against previous versions of the framework, see the page on legacy testing
Genesis provides a Junit 5 extension that helps developers write integration tests. The extension will create a Genesis microservice for the duration of each test case. This page will deal with the GenesisJunit extension in general.
Testing specific services
In addition to general test support; GenesisJunit also provides support for specific services. For more information, have a look at the links below.
A simple example
Let's have a look at a simple test.
We have a TradeProcessor
class that takes a trade Id, processes the trade and updates the status.
We want to make sure our trade is marked as processed, once complete.
- Kotlin
- Java
@ExtendWith(GenesisJunit::class) // 1
@CsvData("my-trades.csv") // 2
class TradeProcessorTest {
@Inject
lateinit var db: SyncEntityDb // 3
@Inject
lateinit var tradeProcessor: TradeProcessor // 4
@Test
fun myTest() {
tradeProcessor.processTrade("TRADE_1") // 5
val dbTrade = db.get(Trade.byId("TRADE_1")) // 6
assert(dbTrade.status == TradeStatus.PROCESSED) // 7
}
}
@ExtendWith(GenesisJunit::class) // 1
@CsvData("my-trades.csv") // 2
public class SimpleEventHandlerTest {
@Inject
private SyncEntityDb db = null; // 3
@Inject
private TradeProcessor tradeProcessor = null; // 4
@Test
public void simpleTestExample() {
tradeProcessor.processTrade("TRADE_1"); // 5
var dbTrade = db.get(Trade.byId("TRADE_1")); // 6
assert(dbTrade.status == TradeStatus.PROCESSED); // 7
}
}
Let's break down what is happening here.
@ExtendWith(GenesisJunit.class)
- using this annotation will enable the Genesis Junit extension. It will manage the Genesis resources during the test.@CsvData("my-trades.csv")
- our test relies on trade data existing in the database. Here we specify that data.@Inject
andSyncEntityDb
- all classes available for injection during runtime, are available in our test. Here we're getting a reference to the database.@Inject
andTradeProcessor
- we can also inject our own classes.TradeProcessor
has a constructor annotated with@Inject
, so the dependency injection mechanism knows how to instantiate it.- By the time the test starts, our injected instances are available and we can start testing.
- We can access the database as we would normally to get the updated state of the trade.
- We verify that the trade status has been updated as expected.
GenesisJunit
GenesisJunit will take care of the Genesis side of the test and will do the following for each test:
- instantiate the database, including applying the schema
- start the database, apply the schema/ truncate tables
- load csv data
- start a Genesis microservice
- inject properties into the test class
- run the test
- clean up resources
Annotation Reference
GenesisJunit supports the following annotations.
Please note that none of these will work without adding @ExtendWith(GenesisJunit::class)
or @ExtendWith(GenesisJunit.class)
to your class or function.
These @ExtendWith(GenesisJunit.class)
and any of the annotations below work on both the class and method level.
Some of the annotations are Repeatable, this means that you can provide multiple values at the same class or method.
If you provide those Repeatable annotations on both the class and method level, both would be applied.
If you provide non-Repeatable annotations on both the class and method level, the method level annotion would take precedence.
The enable annotations (i.e. EnableDataDump and EnableInMemoryTestAuthCache) do not support disabling at the method level once enabled on the class level.
ScriptFile
Use this annotation to load a script file as part of the test. This annotation is Repeatable.
- Kotlin
- Java
@ExtendWith(GenesisJunit::class)
@ScriptFile("my-eventhandler.kts")
internal class ScriptFileExamples {
@Test
fun testScriptFileExamples() {
// just uses "my-eventhandler.kts"
}
@Test
@ScriptFile("other-eventhandler.kts")
fun testMoreScriptFiles() {
// uses "my-eventhandler.kts" and "other-eventhandler.kts"
}
@Test
@ScriptFile("other-eventhandler.kts")
@ScriptFile("more-eventhandler.kts")
fun testManyScriptFiles() {
// uses "my-eventhandler.kts", "other-eventhandler.kts" and "more-eventhandler.kts"
}
}
@ExtendWith(GenesisJunit.class)
@ScriptFile("my-eventhandler.kts")
public class ScriptFileJavaExample {
@Test
public void testScriptFileExamples() {
// just uses "my-eventhandler.kts"
}
@Test
@ScriptFile("other-eventhandler.kts")
public void testMoreScriptFiles() {
// uses "my-eventhandler.kts" and "other-eventhandler.kts"
}
@Test
@ScriptFile("other-eventhandler.kts")
@ScriptFile("more-eventhandler.kts")
public void testManyScriptFiles() {
// uses "my-eventhandler.kts", "other-eventhandler.kts" and "more-eventhandler.kts"
}
}
ConfigFile
Use this annotation is used to load a config file as part of a test. This annoatation is not Repeatable.
- Kotlin
- Java
@ExtendWith(GenesisJunit::class)
@ConfigFile("my-process-config.kts")
internal class ConfigFileExamples {
@Test
fun myProcessConfig() {
// just uses "my-process-config.kts"
}
@Test
@ConfigFile("other-process-config.kts")
fun otherProcessConfig() {
// uses "other-process-config.kts"
}
}
@ExtendWith(GenesisJunit.class)
@ConfigFile("my-process-config.kts")
public class ConfigFileExampleJavaTest {
@Test
public void myProcessConfig() {
// just uses "my-process-config.kts"
}
@Test
@ConfigFile("other-process-config.kts")
public void otherProcessConfig() {
// uses "other-process-config.kts"
}
}
CsvData
This annotation is used to load CSV data as part of a test. This annotation is Repeatable.
- Kotlin
- Java
@ExtendWith(GenesisJunit::class)
@CsvData("my-data.csv")
internal class CsvDataExamples {
@Test
fun loadCsvData() {
// just uses "my-data.csv"
}
@Test
@CsvData("other-data.csv")
fun loadCsvDataFromOtherDataFile() {
// uses "my-data.csv" and "other-data.csv"
}
@Test
@CsvData("other-data.csv")
@CsvData("more-data.csv")
fun loadCsvDataFromMultipleFiles() {
// uses "my-data.csv", "other-data.csv" and "more-data.csv"
}
}
@ExtendWith(GenesisJunit.class)
@CsvData("my-data.csv")
public class CsvDataExamplesJavaTest {
@Test
public void loadCsvData() {
// just uses "my-data.csv"
}
@Test
@CsvData("other-data.csv")
public void loadCsvDataFromOtherDataFile() {
// uses "my-data.csv" and "other-data.csv"
}
@Test
@CsvData("other-data.csv")
@CsvData("more-data.csv")
public void loadCsvDataFromMultipleFiles() {
// uses "my-data.csv", "other-data.csv" and "more-data.csv"
}
}
EnableInMemoryTestAuthCache
This annotation enables the in-memory auth cache.
To set or remove entity authorization, you will need to inject an InMemoryTestAuthCache
into your class.
You can apply this annotation on both classes and methods.
However, once enabled on the class level, there is no way to disable on the method level.
- Kotlin
- Java
@ExtendWith(GenesisJunit::class)
@EnableInMemoryTestAuthCache
internal class EnableInMemoryTestAuthCacheExamples {
/**
* inject [InMemoryTestAuthCache] to programmatically change authorisations
*/
@Inject
lateinit var authCache: InMemoryTestAuthCache
@Test
fun testAuthorizeAndRevokeAuthCache() {
// to authorize:
authCache.authorise(
authMap = "TRADE_VISIBILITY",
entityCode = "00000000001TRSP0",
userName = "JohnDoe"
)
// and then to revoke authorisation:
authCache.authorise(
authMap = "TRADE_VISIBILITY",
entityCode = "00000000001TRSP0",
userName = "JohnDoe"
)
}
}
@ExtendWith(GenesisJunit.class)
@EnableInMemoryTestAuthCache
public class EnableInMemoryTestAuthCacheExamplesJavaTest {
/**
* inject InMemoryTestAuthCache to programmatically change authorisations
*/
@Inject
private InMemoryTestAuthCache authCache = null;
@Test
public void testAuthorizeAndRevokeAuthCache() {
// to authorize:
authCache.builder()
.withAuthMap("TRADE_VISIBILITY")
.withEntityCode("00000000001TRSP0")
.withUserName("JohnDoe")
.authorise();
// and then to revoke authorisation:
authCache.builder()
.withAuthMap("TRADE_VISIBILITY")
.withEntityCode("00000000001TRSP0")
.withUserName("JohnDoe")
.revoke();
}
}
EnableDataDump
This annotation will enable the DATADUMP log level in the microservice. This will output any messages sent and received to the logger. You can apply this annotation on both classes and methods. However, once enabled on the class level, there is no way to disable on the method level.
- Kotlin
- Java
@ExtendWith(GenesisJunit::class)
@EnableDataDump
class DataDumpTest {
@Test
@EnableDataDump
fun myTest() {
// ...
}
}
@ExtendWith(GenesisJunit.class)
@EnableDataDump
public class DataDumpTest {
@Test
@EnableDataDump
public void myTest() {
// ...
}
}
RootLogLevel
This annotation will adjust the log level for the life of the test. You can apply this annotation on both classes and methods. Providing this annotation at the method level will take precedence over the class level annotation.
- Kotlin
- Java
@ExtendWith(GenesisJunit::class)
@RootLogLevel(Level.INFO)
class LogLevelTest {
@Test
fun myTest() {
// root log level set to INFO
}
@Test
@RootLogLevel(Level.TRACE)
fun myOtherTest() {
// root log level set to TRACE
}
}
@ExtendWith(GenesisJunit.class)
@EnableDataDump
public class LogLevelTest {
@Test
public void myTest() {
// root log level set to INFO
}
@Test
@RootLogLevel(Level.TRACE)
public void myOtherTest() {
// root log level set to TRACE
}
}
GenesisHome
Annotation used to specify the Genesis home directory path.
It can be applied to classes or functions/methods.
If the annotation is not provided, genesis home will default to /genesisHome
.
Providing this annotation at the method level will take precedence over the class level annotation.
- Kotlin
- Java
@ExtendWith(GenesisJunit::class)
@GenesisHome("/genesisHomeA")
class GenesisHomeTest {
@Test
fun myTest() {
// this test uses genesisHomeA
}
@Test
@GenesisHome("/genesisHomeB")
fun myOtherTest() {
// this test uses genesisHomeB
}
}
@ExtendWith(GenesisJunit.class)
@GenesisHome("/genesisHomeA")
public class GenesisHomeTest {
@Test
public void myTest() {
// this test uses genesisHomeA
}
@Test
@GenesisHome("/genesisHomeB")
public void myOtherTest() {
// this test uses genesisHomeB
}
}
SysDefOverwrite
Annotation used to indicate that a specific system definition property should be overwritten. This annotation can be applied to classes or functions. When specifying this annotation on both the class and the function level, both will be taken into account, unless the same property is overwritten, in which case the method-level annotation will take precedence.
- Kotlin
- Java
@ExtendWith(GenesisJunit::class)
@SysDefOverwrite("MY_VALUE", "ABC")
internal class OverwriteSysDefTest {
@Test
fun simpleExample() {
// MY_VALUE is set to ABC
}
@Test
@SysDefOverwrite("OTHER_VALUE", "DEF")
fun multipleLevels() {
// MY_VALUE is set to ABC, OTHER_VALUE is set to DEF
}
@Test
@SysDefOverwrite("OTHER_VALUE", "DEF")
@SysDefOverwrite("MY_VALUE", "GHI")
fun overwritingClassLevelSysDef() {
// MY_VALUE is set to GHI, OTHER_VALUE is set to DEF
}
}
@ExtendWith(GenesisJunit.class)
@SysDefOverwrite(key = "MY_VALUE", keyValue = "ABC")
public class OverwriteSysDefTest {
@Test
public void simpleExample() {
// MY_VALUE is set to ABC
}
@Test
@SysDefOverwrite(key = "OTHER_VALUE", keyValue = "DEF")
public void multipleLevels() {
// MY_VALUE is set to ABC, keyValue = OTHER_VALUE is set to DEF
}
@Test
@SysDefOverwrite(key = "OTHER_VALUE", keyValue = "DEF")
@SysDefOverwrite(key = "MY_VALUE", keyValue = "GHI")
public void overwritingClassLevelSysDef() {
// MY_VALUE is set to GHI, OTHER_VALUE is set to DEF
}
}
PackageScan
Annotation used to mark classes or functions for package scanning. This annotation should be used to mark classes or functions that require package scanning. It is used to specify the marker interface or class to be used for scanning packages. This annotation is Repeatable.
- Kotlin
- Java
@ExtendWith(GenesisJunit::class)
@PackageScan(MyClass::class)
internal class PackageScanTest {
@Test
fun myTest() {
// scans the package of MyClass
}
@Test
@PackageScan(MyOtherClass::class)
fun myOtherTest() {
// scans the package of MyClass and MyOtherClass
}
@Test
@PackageScan(MyOtherClass::class)
@PackageScan(MyThirdClass::class)
fun myThirdTest() {
// scans the package of MyClass, MyOtherClass and MyThirdClass
}
}
@ExtendWith(GenesisJunit.class)
@PackageScan(MyClass.class)
public class PackageScanTest {
@Test
public void myTest() {
// scans the package of MyClass
}
@Test
@PackageScan(MyOtherClass.class)
public void myOtherTest() {
// scans the package of MyClass and MyOtherClass
}
@Test
@PackageScan(MyOtherClass.class)
@PackageScan(MyThirdClass.class)
public void myThirdTest() {
// scans the package of MyClass, MyOtherClass and MyThirdClass
}
}
ProvidedInstance
Annotation used to mark properties or methods as provided instances. Marked properties or methods should be used to provide instances during runtime.
- Kotlin
- Java
@ExtendWith(GenesisJunit::class)
internal class ProvidedInstanceTest {
/**
* Will provide [MyClass] as an instance of [MyInterface] during the dependency injection phase.
*/
@ProvidedInstance
private val myInstance: MyInterface = MyClass()
@Test
fun myTest() {
//
}
}
@ExtendWith(GenesisJunit.class)
public class ProvidedInstanceTest {
/**
* Will provide [MyClass] as an instance of [MyInterface] during the dependency injection phase.
*/
@ProvidedInstance
private MyInterface myInstance = new MyClass();
@Test
public void myTest() {
//
}
}