Skip to main content

Jamulator

Jamulator stands for Javascript based Emulator. The Jamulator allows mocking external services that are not under control and Javascript is used to configure the behaviour of Jamulator.

Architecture

Jamulator has two interfaces:

  • A controller interface - Allows setting up rules
  • The main interface - Emulating external systems

Jamulator flow


Based on the request in 2.1 Jamulator will behave according to rules that were configured in step 1. The tester of course needs to know what the system expects as response for the given test case.

Rules are processed in order of how they were created. For each rule a when function is executed. If true is returned there is a match and the do function is then executed.

Both the when and do functions are javascript functions. Under the hood the Nashorn engine is used. Currently, it implements the ECMAScript 5.1 specification that is quite outdated and some common functions are not available.

The javascript code has access to a limited DSL. That DSL allows reading the request and creating a response or sending other requests to other servers plus a set of helper functions such as XML parsing and signing. When communicating to downstream servers a set of authentication mechanisms is also available.

Here is an example of complex test that includes setup of a rule on Jamulator

class TestClass {

@Test
public void sampleTest(
@Capability(key = "browserName", value = "pn5-jamulator")
@Capability(key = "webdriver.url", value = "https://controller.jamulator.mydomain.com:4444")
JamulatorController jamulatorController,
@Capability(key = "browserName", value = "pn5-driver8")
HttpApplication httpApplication) {

jamulatorController
.createNewRule()
.withName("respond200allTheTime")
.withWhenFunction("true")
.withDoFunction("jam.createResponse(200, null, null)")
.activateRule(true);

httpApplication
.prepareRestRequest("https://jamulator.mydomain.com/", "GET")
.sendAndGetResponse()
.assertStatus(200);

}

}

The fist block of code sets a rule that will respond to any request with a http response with a response code 200, no headers and empty body content. The second block sends a http request to the jamulator main url using the method GET. The test verifies that a 200 response is received.

Here the test calls directly the Jamulator which in fact is not a real use case because here we could mock the response directly in code. In the real setup Jamulator will be called by some backend component after the test acts on the frontend. The backend component needs to be configured to communicate with Jamulator instead of some external service.

Wrapping in custom objects

Instead of using the predefined methods in the JamulatorController interface, it is possible to define a custom interface that extends JamulatorController and to wrap the predefined actions into more business oriented.

Example:

class TestClass {

@Test
public void sampleTest(MyJamulatorController jamulator,
MyHttpApplication httpClient) {

jamulator
.activateResponse200ForAll();

httpClient
.verifyThatReseponse200IsReceived();

}

}

@Capability(key = "browserName", value = "pn5-jamulator")
@Capability(key = "webdriver.url", value = "https://controller.jamulator.mydomain.com:4444")
public interface MyJamulatorController extends JamulatorController {

default MyJamulatorController activateResponse200ForAll() {

createNewRule()
.withName("respond200allTheTime")
.withWhenFunction("true")
.withDoFunction("jam.createResponse(200, null, null)")
.activateRule(true);

return null;
}
}

@Capability(key = "browserName", value = "pn5-driver8")
public interface MyHttpApplication extends HttpApplication {

default MyHttpApplication verifyThatReseponse200IsReceived() {

prepareRestRequest("https://jamulator.mydomain.com/", "GET")
.sendAndGetResponse()
.assertStatus(200);

return null;
}
}

This pattern is common for all domains handled by Pumpo#5 and is the recommended coding style to keep tests readable also for people not having deeper knowledge of implementation details.

Why Jamulator

There is a number of other frameworks for mocking systems. Following are the features that would not be found elsewhere:

  • Turing complete rules DSL: Jamulator allows configuring rules using Javascript as a commonly known and turing complete.
  • Asynchronous rules: Jamulator allows configuring any asynchronous behaviour including forward communication to downstream services.
  • Rule specific context: Jamulator runs each rule in a context that is persisted across rules execution allowing more complex behaviour (e.g. counters) to be programmed.
  • Non-blocking high-performance: Jamulator is based on a non-blocking scheme and will be able to deliver high performance even on small infrastructure.
  • Integrated controller interface in Pumpo5: Jamulator is seamlessly integrated in Pumpo5 which allows setting up rules right from the test without any other boilerplate code.

Current limitations

Jamulator is still under active development. Following are the most important limitations but as we make progress we will clear them out:

  • Rules cannot be read once set, only unset
  • Rules are not persisted on disk and restarting Jamulator means losing the state
  • There is not yet any API to verify which rules were executed and what was the result

Rule setup API

JamulatorController::createNewRule

Starts a builder for a new rule

The HttpRequestBuilder is a generic builder. Usually one will prefer using either RestRequestBuilder of SoapRequestBuilder by using the respective entry methods below.

JamulatorController::deleteRule

ParameterTypeDescription
ruleNameStringThe string id of the rule to delete
ignoreMissingbooleanIgnore the error that no such rule was found

Deletes a previously created rule based on its id (name). In case the flag ignoreMissing is set to false it will throw an exception if the rule is not found.

....

Development guide

Developing the rules in javascript is not difficult but there are a few small challenges that this guide should help overcome.

...

Javascript context API

jam

jam.request

...