citrusframework / citrusframework/citrus

async actions fail without behavior wrapper

Open
#542 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Java
Stars
485
Forks
155
Avg merge
4d 22h
Merged PRs (30d)
6

Description

**Root cause analysis outstanding**

**Citrus Version**
2.7.8

**Expected behavior**
When I use a async container, it doesn't matter if I wrap a single `TestAction` in a `AbstractTestBehavior` or not.

**Actual behavior**
It matters, because the test fails, if I don't wrap my single jms `TestAction`.

**Test case sample**
Unfortunately the test case is not that simple.

Passing test case - with behavior wrapper:
```java
public class TodoListIT extends TestNGCitrusTestRunner {

@Autowired
private HttpClient httpClient;

@Autowired
private HttpServer httpServer;

@Autowired
private JmsEndpoint jms;

private String payload = "{\"foo\": 123}";

@Test
@CitrusTest
public void testGet() {
async().actions(
simulateThirdPartyRestApi(),
verifyForwardToInternalJms());

simulateSUT();
}

private void simulateSUT() {
//Request a payload from a third party REST API
http(http -> http
.client(httpClient)
.send()
.post("/bar"));
http(http -> http
.client(httpClient)
.receive()
.response()
.status(HttpStatus.OK)
.payload(payload));

//Forward the payload into a internal JMS
send(action -> action.endpoint(jms).payload(payload));
}

// ISSUE OCCURS HERE
private ApplyTestBehaviorAction verifyForwardToInternalJms() {
return applyBehavior(new AbstractTestBehavior(){
@Override
public void apply() {
receive(action -> action.endpoint(jms).payload("{\"foo\": 123}"));
}
});

}

private ApplyTestBehaviorAction simulateThirdPartyRestApi() {
return applyBehavior(new AbstractTestBehavior(){
@Override
public void apply() {
http(http -> http.server(httpServer)
.receive()
.post("/bar"));

http(http -> http.server(httpServer)
.respond()
.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
.payload(payload));
}
});
}
}
```

Failing test case - without behavior wrapper:
```java
public class TodoListIT extends TestNGCitrusTestRunner {

@Autowired
private HttpClient httpClient;

@Autowired
private HttpServer httpServer;

@Autowired
private JmsEndpoint jms;

private String payload = "{\"foo\": 123}";

@Test
@CitrusTest
public void testGet() {
async().actions(
simulateThirdPartyRestApi(),
verifyForwardToInternalJms());

simulateSUT();
}

private void simulateSUT() {
//Request a payload from a third party REST API
http(http -> http
.client(httpClient)
.send()
.post("/bar"));
http(http -> http
.client(httpClient)
.receive()
.response()
.status(HttpStatus.OK)
.payload(payload));

//Forward the payload into a internal JMS
send(action -> action.endpoint(jms).payload(payload));
}

// ISSUE OCCURS HERE
private TestAction verifyForwardToInternalJms() {
return receive(action -> action.endpoint(jms).payload("{\"foo\": 123}"));

}

private ApplyTestBehaviorAction simulateThirdPartyRestApi() {
return applyBehavior(new AbstractTestBehavior(){
@Override
public void apply() {
http(http -> http.server(httpServer)
.receive()
.post("/bar"));

http(http -> http.server(httpServer)
.respond()
.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
.payload(payload));
}
});
}
}
```

Config:
```java
@Configuration
public class EndpointConfig {

@Bean
public HttpClient todoClient() {
return CitrusEndpoints.http()
.client()
.requestUrl("http://localhost:8080")
.build();
}

@Bean
public HttpServer todoServer() {
return CitrusEndpoints.http()
.server()
.port(8080)
.timeout(120000L)
.autoStart(true)
.build();
}

@Bean
public JmsEndpoint createTopicJmsEndpoint() throws JMSException {
return CitrusEndpoints.jms()
.asynchronous()
.destination("foo")
.connectionFactory(new ActiveMQConnectionFactory("tcp://localhost:61616"))
.timeout(120000)
.pubSubDomain(true)
.autoStart(true)
.build();
}
}
```

JMS Broker setup:
```xml
5.15.2
...

org.apache.activemq.tooling
activemq-maven-plugin
${activemq.version}

false


log4j.configuration
log4j.properties




io.fabric8
docker-maven-plugin
0.26.0

default
true


activemq-broker
consol/activemq-5.12:latest

alias

61616:61616
8161:8161


Broker startup

http://localhost:8161
GET
200


500


true
green






```

**Additional Information**
1. The original test case was reconstructed with an IBM-MQ.

2. If I remove the async container and use a simple `TestAction` for my jms action, the test passes! So without async it doesn't matter if I use a behavior or not.

```java
public class TodoListIT extends TestNGCitrusTestRunner {

@Autowired
private HttpClient httpClient;

@Autowired
private HttpServer httpServer;

@Autowired
private JmsEndpoint jms;

private String payload = "{\"foo\": 123}";

@Test
@CitrusTest
public void testGet() {

// SUT Simulation:
// Request a payload from a third party REST API
http(http -> http
.client(httpClient)
.send()
.post("/bar")
.fork(true));

simulateThirdPartyRestApi();

// SUT Simulation:
// Receive the payload from the third party REST API
http(http -> http
.client(httpClient)
.receive()
.response()
.status(HttpStatus.OK)
.payload(payload));

// SUT Simulation:
// Forward the payload into a internal JMS
send(action -> action.endpoint(jms).payload(payload));

verifyForwardToInternalJms();
}

private TestAction verifyForwardToInternalJms() {
return receive(action -> action.endpoint(jms).payload("{\"foo\": 123}"));
}
// private ApplyTestBehaviorAction verifyForwardToInternalJms() {
// return applyBehavior(new AbstractTestBehavior(){
// @Override
// public void apply() {
// receive(action -> action.endpoint(jms).payload("{\"foo\": 123}"));
// }
// });
//
// }

private ApplyTestBehaviorAction simulateThirdPartyRestApi() {
return applyBehavior(new AbstractTestBehavior(){
@Override
public void apply() {
http(http -> http.server(httpServer)
.receive()
.post("/bar"));

http(http -> http.server(httpServer)
.respond()
.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
.payload(payload));
}
});
}
}
```

Contributor guide

Open the contributing guide

Research direction

Start with the async().actions setup in the provided TestNGCitrusTestRunner example and compare the wrapped and unwrapped verifyForwardToInternalJms() cases. Reproduce the failure with the JMS endpoint configuration, then identify why both forms do not behave equivalently; done means the unwrapped TestAction passes in the async container as it does without async.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
testing
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.