Hot questions for Using Cucumber in citrus framework

Top Java Programmings / Cucumber / citrus framework

Question:

Suppose I have a test like below (using cucumber) -

Scenario: Login successfully to Facebook
  Given the user test exists
  And user name and password is entered
  When the login form is submitted
  Then I expose a HTTP-Rest service to validate the user name and password
  When I receive a validation success message
  Then display the welcome message to the user

Here, when "the login form is submitted" is called, it submit the request to a HTTP REST service which will pass the user name and password to another HTTP Rest Service (that would be exposed by Citrus Framework) using "I expose a HTTP-Rest service to validate the user name and password" which will validate the data and send a success response. Therefore step definitions for "the login form is submitted" and "I expose a HTTP-Rest service to validate the user name and password" should be executed asynchronously.

Could you please help me - how I could achieve this using cucumber (or/and citrus).

Note: I'm not using any stub application to expose the HTTP Rest service for "I expose a HTTP-Rest service to validate the user name and password"; I'm trying to expose the service using Citrus framework.

Step definitions are written in java.


Answer:

First of all you need to setup the citrus-cucumber extension in your project. Then you should be able to use @CitrusResource annotation that injects a test runner instance to your steps class:

@CitrusResource
private TestRunner runner;

Also you can inject the http server instance that should receive the request.

@CitrusEndpoint(name = "userServer")
private HttpServer userServer;

Then you can use test runner and the server to receive the request and send the response in a step definition:

@Then("^I expose a HTTP-Rest service to validate the user name and password$")
public void exposeHttpRestService() {
    runner.http(http -> http.server(userServer)
        .receive()
        .post()
        .payload("{\"username\": \"test\", \"password\": \"secret\"}"));

    runner.http(http -> http.server(userServer)
        .send()
        .response(HttpStatus.OK));
}

The login form should be submitted in a separate step definition using a separate thread in order to create asynchronous nature:

@When("^the login form is submitted$")
public void submitForm() {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.submit(() -> {
        // do submit the form
    });    
}

Question:

I am testing REST services using Citrus Java DSL. I would like to save data from the response for advance operations. According to the documenation, I should use Citrus test context for this purposes.

I've tried to inject TestContext with CitrusResource annotation:

@CitrusResource
private TestRunner runner;

@CitrusResource
private TestContext context;


@When("^service sends request to get all orders$")
public void get_Orders() {
    runner.http(action -> action.client(httpClientName)
            .send()
            .get(basePath));   
}

@Then("^Service gets response with preflight id: \"([^\"]*)\"$")
public void verify_Orders(String preflightId) {
        runner.http(action -> action.client(httpClientName)
            .receive()
            .response(HttpStatus.OK)
            .contentType("application/json;charset=UTF-8")
            .extractFromPayload("$[*].styleId", "ids")
            .validate("$[*].styleId", everyItem(not(isEmptyOrNullString())))
            .validate("$[*].styleId", hasItem(preflightId)));

    String ids = context.getVariable("${ids}", String.class);
}

But got Null Pointer exception

1 Scenarios (1 failed)
4 Steps (1 failed, 1 skipped, 2 passed)
0m5.541s

java.lang.NullPointerException

Also, I've tried to create new TestContext in required method

@CitrusResource
private TestRunner runner;

@CitrusResource
private TestContext context;


@When("^service sends request to get all orders$")
public void get_Orders() {
    runner.http(action -> action.client(httpClientName)
            .send()
            .get(basePath));   
}

@Then("^Service gets response with preflight id: \"([^\"]*)\"$")
public void verify_Orders(String preflightId) {
TestContext context = new TestContext();
        runner.http(action -> action.client(httpClientName)
            .receive()
            .response(HttpStatus.OK)
            .contentType("application/json;charset=UTF-8")
            .extractFromPayload("$[*].styleId", "ids")
            .validate("$[*].styleId", everyItem(not(isEmptyOrNullString())))
            .validate("$[*].styleId", hasItem(preflightId)));

    String ids = context.getVariable("${ids}", String.class);
}

And got

com.consol.citrus.exceptions.CitrusRuntimeException: Unknown variable 'ids'

    at com.consol.citrus.context.TestContext.getVariableObject(TestContext.java:158)
    at com.consol.citrus.context.TestContext.getVariable(TestContext.java:133)
    at com.consol.citrus.context.TestContext.getVariable(TestContext.java:122)

Could you please help me with it ?


Answer:

I'm not familiar with Citrus and I'm not sure which documentation you're referring to. To share state between steps in Cucumber-jvm, it's recommended to use Dependency Injection (DI). There are several options for DI with Cucumber, including Spring, Guice and PicoContainer. The latter is a light weight DI framework, which is probably the best option if you're not using a different DI framework.

Question:

Consider a scenario with multiple "Then" step definitions meant to be used as assertions against a response payload:

  ...
  When a response is received
  Then the response should have an element "foo" with the content "bar"
  And the response should contain 1 "foobar" element
  And the response should have an element "rab" with the content "oof"
  ...

What is the intended way for Citrus to handle an unknown amount of validations? Can you define several validators before calling receive()? Can this be handled with a validationCallback() and minimal Gherkin rewrite?

The current implementation uses validationCallback() to store the payload as an instance variable, then validate against the variable. However, it would be much better to leverage the power of Citrus.


Answer:

You could use named messages in a Given section or even in a scenario background:

Given message fooResponse
    And <fooResponse> payload is "Hi my name is Foo!"
    And <fooResponse> header operation is "sayHello"
    ...

You can use the named message in a scenario like this:

Scenario: Send and receive
  When <client> sends message <fooRequest>
  Then <client> should receive message <fooResponse>

The validation of all elements is done automatically as the Citrus validator compares received and expected message contents.

Also you could extract the message creation to a Java POJO message creator:

Background:
  Given message creator com.consol.citrus.FooMessageCreator

public class FooMessageCreator {
    @MessageCreator("fooResponse")
    public Message createEchoResponse() {
        return new DefaultMessage("Hi my name is Foo!")
                    .setHeader("operation", "sayHello");
    }
}

In case you need to deal with large XML/Json payloads you can also load that payloads from external file template resource. Instead of setting each element in a separate explicit Then statement you should go for template based compare validation that can also use @ignore@ sections for ignoring parts of the message that are not relevant for validation.

Question:

I'm now using citrus framwork integrated with cucumber. I'm thinking to change for the XML DSL (or java DSL). Should I add a template for each different tested file (xml or json)? And to link them ?

My tests should compare two files or more. I can just put the path of theses files ( I saw the examples are just about messages..) ?

Thanks a lot for your help!


Answer:

In case you want to use Cucumber BDD with Citrus you should go with the Java DSL as it is integrated into writing Cucumber step definition classes. Within the step definition method you can of course load templates from filesystem or classpath. Lets say you have the following line in your BDD specification.

When user adds entry "path/to/template.txt"

You can use the path in your step definition as method parameter and load the template from filesystem or classpath.

@When("^user adds entry \"([^\"]*)\"$")
public void add_entry(String path) {
    designer.http()
        .client(todoClient)
        .send()
        .post("/todo")
        .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
        .payload(new ClassPathResource(path));
}

Of course you can also do this when receiving and validating messages via file templates.

In case you go for pure XML tests without Cucumber you can just give the file path in your send/receive operation.

<testcase name="TodoList_Post_IT">
  <actions>
    <http:send-request client="todoClient">
      <http:POST path="/todo">
        <http:headers content-type="application/x-www-form-urlencoded"/>
        <http:body>
          <http:resource file="path/to/template.txt" />
        </http:body>
      </http:POST>
    </http:send-request>
  </actions>
</testcase>

This works for Http and all other message transports (JMS, WebSocket, SOAP WebServices, ...).

You can also use test variables in your template files in order to reuse them in multiple test cases. Have a look at test variables in Citrus they help you to get more dynamic tests data.