Simulating Services With SV Lab in JUnit Tests Run With Maven

Watch a video explaining how to leverage simulated REST service in JUnit tests run with Maven:

Using SV Lab in JUnit/Maven tests to remove 3rd party dependencies

Service virtualization technique helps unit testing the application components by resolving dependencies on backend services. This topic describes how to simulate a REST service that is configured and launched from a JUnit test run with Maven.

The example application

We use the demo application that is available on GitHub.

The application being tested is controlling an irrigation system in a clever way. It conserves water consumption by reducing the amount of water used for irrigation according to the 24-hour forecast of precipitations.

The goal of testing the application logic is verifying its behavior under conditions of rainy weather when the irrigation should conserve water as well as on a dry day when the irrigation amount should not be reduced.

There are two scenarios simulating different weather forecast for each of the two tests:

@Test
public void testWaterSavingOnRainyDay() throws Exception {
    sv.runSimulation("rainyDay");
    ...
}

@Test
public void testIrrigationOnDryDay() throws Exception {
    sv.runSimulation("dryDay");
    ...
}

Integration testing

The JUnit test for the IrrigationLogic is accompanied by simulation models and virtual lab configuration. The test compiles model sources and deploys the virtual lab in the initialization phase. The WeatherClient is configured to use a HTTP proxy connector in virtual lab when talking to the WeatherService directing it to virtual service. The virtual service responds according to running scenario simulating desired conditions for every test case.

Application Model

The SmartIrrigationTest contains two test cases, each requiring different weather forecast to be simulated so we have two scenarios in the application model in src/main/resources/demo/SmartIrrigationApplicationModel.js:

@sv.applicationScenario
rainyDay() {
    this.weatherForecastServiceModel.rainyDayMeteogram();
}

@sv.applicationScenario
dryDay() {
    this.weatherForecastServiceModel.dryDayMeteogram();
}

Service Model

The actual behavior of the weather forecast service is defined in the src/main/resources/demo/WeatherForecastServiceModel.js. The WeatherClient performs two REST calls to obtain current forecast. First it gets the ID of latest weather forecast run at the Czech Hydrometeorological Institute's supercomputer:

this.service.GET("/apirun")
    .withRequest()
    .withResponse({
        "id": "190515_12"
    }, sv.JSON)                                                 
    ...
Then it fetches the actual forecast data in a second call:
        this.service.GET("/apimeteogram")
            .withRequest()
                .withQueryParameters(sv.svVar({}))                   
            .withResponse({
                "data": {
                    "rain_dif": {
                        "max": 10.0,
                        "values": [
                            2.1297, 1.2324, 2.4592, 2.5837, 2.5626, 1.4771,
                            ...

The real service at http://www.medard-online.cz returns much more forecast information but we kept our model simple by mocking just the data actually consumed by WeatherClient in our application - the forecast run ID and the rain_dif precipitation forecast in particular.

Virtual Lab

In order to run and use the simulation, we have to configure and start a virtual lab. The virtual lab can be configured using a JSON file or it can be set-up and controlled over the SV API.

In this example, we load the lab configuration from the src/main/resources/sv-lab.json file and compile the module from application and service model sources (SmartIrrigationApplicationModel.js and WeatherForecastServiceModel.js):

final String labConfigPath = "classpath:/sv-lab.json";
final String vslPath = "classpath:/demo/*";
sv.loadActiveVirtualLab(labConfigPath, sv.compileModuleFromSources(vslPath), true);

It is convenient to keep the VSL sources and sv-lab.json file in the resources directory and access them with "classpath:/..." Spring resource locators. Alternatively, you can use relative ("file:target/test-classes/sv-lab.json") or even absolute ("file:///c:/test/sv-demos/smart-irrigation-maven/target/test-classes/sv-lab.json") paths to locate the files.

In the configuration file we define the virtual lab where all the services will run and pick the application model to use:

"virtualLab": [
  {
    "id": "virtual-lab:9001",
    "displayName": "Smart Irrigation Test Virtual Lab",
    "applicationModelId": "SmartIrrigationApplicationModel",
    "connector": ...,
    "virtualServiceInstance": ...
  }
]

Then we create a HTTP proxy connector providing connectivity for the REST virtual service we want to simulate.

"connector": [
  {
    "id": "connector",
    "connectorType": "httpProxy",
    "properties": {
      "bindPort": 9001
    }
  }
]

Here we create a service instance specifying the service url where it will be exposed and choosing the SIMULATE_SERVICE simulation mode.

"virtualServiceInstance": [
  {
    "virtualServiceId": "WeatherForecastServiceModel.RestServiceInterface",
    "serviceMode": "SIMULATE_SERVICE",
    "endpoints": [
      {
        "properties": {
          "httpServiceUrl": "http://www.medard-online.cz"
        },
        "connectorId": "httpConnector"
      }
    ]
  }
]

We start the virtual lab:

sv.startActiveVirtualLab();

Then we are ready to simulate chosen scenarios in each test method:

@Test
public void testWaterSavingOnRainyDay() throws Exception {
    sv.runSimulation("rainyDay");
    ...
}

...

@Test
public void testWaterSavingOnDryDay() throws Exception {
    sv.runSimulation("dryDay");
    ...
}