/JoUnit

Unit-Test stage orchestrator in Jolie (using Docker and JolieTestSuite)

Primary LanguageJolieMIT LicenseMIT

JoUnit

Tool used for Unit Test of Jolie Microservices

1 - Requirement

1.1 - Format of init.ol

1.2 - Format of test.ol

2 - JoUnit Configuration

3 - JoUnit Usage

1 - Requirement

  1. Microservice to test (re)named as "main.ol"

  2. Directory "test_suite" in the project to test's repository. Within this directory you must put every file needed for the test:

    • init.ol (needed) -> here you will write only a list of goal (format explained below)
    • <testname>.ol -> test's code (you can recursively write goal to another .ol here)
    • dependencies.ol.test (facultative) -> here you will write variables needed for the test
    • every other file needed for the test (such for example as a JSON with some structured data needed for an operations)
  3. You need JolieLang and Docker installed on your computer

  4. You need a Running Docker Container of Jocker. How to correct run jocker HERE

1.1 - Format of init.ol:

The init.ol is simply a list of goal to test surrounded with a run( request )( response ) { ... } block. Every goal point to a file with test's code inside.

Example of init.ol for 3 separate testing file (<testname1>.ol, <testname2>.ol, <testname3>.ol):

    main{
    run( request )( response ){

        grq.name = "<testname1>";
        goal@GoalManager( grq )( testResponse );

        gorq.name = "<testname2>";
        goal@GoalManager( grq )( testResponse );

        grq.name = "<testname3>";
        goal@GoalManager( grq )( testResponse );
     }

Here we execute 3 goal, calling each test we wrote.

For "Goal" we mean something that needs to be executed successfully for proceeding to the next goal. Every goal should return SUCCESS or FAILED (with a fault message in that case)

If, for example, <testname2>'s goal has a fault, it recursively stop every super-goal in waiting.

1.2 - Format of a <testname.ol>

If you need dependencies.ol.test file, you must include it in <testname>.ol

include "./test_suite/dependencies.ol.test"

Format of dependencies.ol.test:

constants {
  nameConst1=<valueConst1>,
  nameConst2=<valueConst2>,
  ...
}

If your microservice has dependencies, you must include every of it.

The syntax for importing a dependency is:

include "<outputPortName>.depservice"

For dependency we mean an external microservice connected with an output port with our microservice to test.

In the <testname>'s main we can write our test, that needs to be surrounded with run( request )( response ) { ... } block.

main{
    run( request )( response ){
        /*First operation test*/
        goalrq.request_message = <operation1's Request>;
        goalrq.name = "/<inputPort's Name>/<operation1's Name>";

        // If you DONT'T NEED dependency
        goal@GoalManager( grq )( testResponse );

        // If you NEED a dependency with an operation named "twice"
        { goal@GoalManager( grq )( testResponse ) | twice( request )( response ){ response = <what dependency should respond> };

        expectedResponse = <operation1's expected response>;
        if( testResponse != expectedResult ){
          fault.message = <significative error message's string>;
          fault.faultname = <fault's name>;
          throw( ExecutionFault, fault)
        }
     }

In the node .request_message we put the input of <operation1>

goalrq.request_message = <operation1's Request>;

In the node .name we put the name of the input port on which we can find our service and the operation we need to test

goalrq.name = "/<inputPort's Name>/<operation1's Name>";

There are two ways of calling a goal:

  • If you DONT'T NEED dependency for this operation's test we simply call a goal
goal@GoalManager( grq )( testResponse );
  • If you NEED a dependency with an operation named "twice" for this operation's test, we need to call a goal and in parallel we need to provide the dependency's operation needed for the goal
{ goal@GoalManager( grq )( testResponse ) | twice( request )( response ){ response = <what dependency should respond> };

When we receive the testResponse, we have to compare it with an expectedResult. If testResponse and expectedResult don't match we throw a fault that will stop recursively every super-goal.

An example easy to understand can be found here

2 - JoUnit Configuration

After cloning JoUnit's repository, you maybe need to edit the config.ini in the root directory.

  • JockerLocation=socket://localhost:8008 if you have configured Jocker's container as written in this article. You must change the port if you have configured Jocker with a different port on your PC's side.

  • OrchestratorLocation=socket://<IPv4 address>:<port> is the local IP address and port on which microservice's container will send the output and on which the orchestrator are waiting. You can change the port, but for the IP address you have to check on which local IP docker container can communicate with the host machine:

    • for MacOS run ifcongif en0 | grep inet. First output line is the IPv6 address while second's is the IPv4 one. In OrchestratorLocation you have to write, in , the address in the second line.
    • for Linux run ifcongif docker0 | grep inet. Output is the IPv4 address that you have to write in in OrchestratorLocation

3 - JoUnit Usage

This tool works only with a clonable repository. This tool supports local repository (both absolute and relative path) and online repository ( URL )

To run the tool go in JoUnit cloned directory and run ./JoUnit <repo path or URL>.

You can also write a Script with a list of similar command and different address