Skip to content

Commit

Permalink
#38 Cucumber-based rest api call testing framework
Browse files Browse the repository at this point in the history
  • Loading branch information
spuliaiev-sfdc committed Aug 3, 2018
1 parent e23de2c commit 46546a9
Show file tree
Hide file tree
Showing 9 changed files with 321 additions and 0 deletions.
25 changes: 25 additions & 0 deletions ui/web/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>${project.build.sourceEncoding}</project.reporting.outputEncoding>
<commons_lang.version>3.6</commons_lang.version>
<cucumber.java.version>1.2.5</cucumber.java.version>
<java.version>1.8</java.version>
</properties>

Expand Down Expand Up @@ -238,6 +239,30 @@
<artifactId>batik-svggen</artifactId>
</dependency>

<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-core</artifactId>
<version>${cucumber.java.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.java.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-junit</artifactId>
<version>${cucumber.java.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-spring</artifactId>
<version>${cucumber.java.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package gallerymine.test.ui;

import org.junit.runner.RunWith;

import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/resources")
public class CucumberIntegrationTest extends SpringIntegrationTest {

}
12 changes: 12 additions & 0 deletions ui/web/src/test/java/gallerymine/test/ui/CucumberLocalTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package gallerymine.test.ui;

import cucumber.api.CucumberOptions;
import cucumber.api.java.Before;
import cucumber.api.junit.Cucumber;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/resources")
public class CucumberLocalTest extends SpringIntegrationTest {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package gallerymine.test.ui;

import org.springframework.http.HttpHeaders;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.web.client.RequestCallback;

import java.io.IOException;
import java.util.Map;

public class HeaderSettingRequestCallback implements RequestCallback {
final Map<String, String> requestHeaders;

private String body;

public HeaderSettingRequestCallback(final Map<String, String> headers) {
this.requestHeaders = headers;
}

public void setBody(final String postBody) {
this.body = postBody;
}

@Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
final HttpHeaders clientHeaders = request.getHeaders();
for (final Map.Entry<String, String> entry : requestHeaders.entrySet()) {
clientHeaders.add(entry.getKey(), entry.getValue());
}
if (null != body) {
request.getBody().write(body.getBytes());
}
}
}
29 changes: 29 additions & 0 deletions ui/web/src/test/java/gallerymine/test/ui/ResponseResults.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package gallerymine.test.ui;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;

import org.apache.commons.io.IOUtils;
import org.springframework.http.client.ClientHttpResponse;

public class ResponseResults {
private final ClientHttpResponse theResponse;
private final String body;

ResponseResults(final ClientHttpResponse response) throws IOException {
this.theResponse = response;
final InputStream bodyInputStream = response.getBody();
final StringWriter stringWriter = new StringWriter();
IOUtils.copy(bodyInputStream, stringWriter);
this.body = stringWriter.toString();
}

ClientHttpResponse getTheResponse() {
return theResponse;
}

String getBody() {
return body;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package gallerymine.test.ui;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import cucumber.api.java.Before;
import gallerymine.GalleryMineApplication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

//@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = GalleryMineApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
@ContextConfiguration
public class SpringIntegrationTest {
static ResponseResults latestResponse = null;
static ResponseEntity<HashMap> latestResponseMap = null;

@LocalServerPort
public int serverPort;

@Autowired
protected RestTemplate restTemplate;

void executeGet(String url) throws IOException {
final Map<String, String> headers = new HashMap<>();
headers.put("Accept", "application/json");
final HeaderSettingRequestCallback requestCallback = new HeaderSettingRequestCallback(headers);
final ResponseResultErrorHandler errorHandler = new ResponseResultErrorHandler();

restTemplate.setErrorHandler(errorHandler);
latestResponse = restTemplate.execute(url, HttpMethod.GET, requestCallback, response -> {
if (errorHandler.hadError) {
return (errorHandler.getResults());
} else {
return (new ResponseResults(response));
}
});
}

void executePost() throws IOException {
final Map<String, String> headers = new HashMap<>();
headers.put("Accept", "application/json");
final HeaderSettingRequestCallback requestCallback = new HeaderSettingRequestCallback(headers);
final ResponseResultErrorHandler errorHandler = new ResponseResultErrorHandler();

if (restTemplate == null) {
restTemplate = new RestTemplate();
}

restTemplate.setErrorHandler(errorHandler);
latestResponse = restTemplate
.execute("http://localhost:"+serverPort+"/baeldung", HttpMethod.POST, requestCallback, response -> {
if (errorHandler.hadError) {
return (errorHandler.getResults());
} else {
return (new ResponseResults(response));
}
});
}

private class ResponseResultErrorHandler implements ResponseErrorHandler {
private ResponseResults results = null;
private Boolean hadError = false;

private ResponseResults getResults() {
return results;
}

@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
hadError = response.getRawStatusCode() >= 400;
return hadError;
}

@Override
public void handleError(ClientHttpResponse response) throws IOException {
results = new ResponseResults(response);
}
}
}
102 changes: 102 additions & 0 deletions ui/web/src/test/java/gallerymine/test/ui/StepDefsIntegrationTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package gallerymine.test.ui;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import cucumber.api.java.en.Given;
import gallerymine.model.mvc.SourceCriteria;
import gallerymine.model.support.PictureGrade;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;

import cucumber.api.java.en.And;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.HashMap;

public class StepDefsIntegrationTest extends SpringIntegrationTest {

@When("^the client calls /baeldung$")
public void the_client_issues_POST_hello() throws Throwable {
executePost();
}

@Given("^the client calls /hello$")
public void the_client_issues_GET_hello() throws Throwable {
executeGet("http://localhost:"+serverPort+"/hello");
}

@Given("^api call /listFolders")
public void the_client_issues_list_folders() {
SourceCriteria searchCriteria = new SourceCriteria();
searchCriteria.setPage(0);
searchCriteria.setSize(10);
searchCriteria.setPath("test");
searchCriteria.setGrade(PictureGrade.GALLERY);
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Accept", "application/json");
headers.add("Content-Type", "application/json; charset=UTF-8");
HttpEntity<SourceCriteria> request = new HttpEntity<>(searchCriteria, headers);
latestResponseMap = restTemplate .exchange("http://localhost:"+serverPort+"/sources/findPath", HttpMethod.POST, request, HashMap.class);
System.out.print("Run");
}

@When("^the client calls /version$")
public void the_client_issues_GET_version() throws Throwable {
executeGet("http://localhost:"+serverPort+"/version");
}

@Then("^response status code is (\\d+)$")
public void the_client_receives_status_code_of(int statusCode) {
final HttpStatus currentStatusCode = latestResponseMap.getStatusCode();
assertThat("status code is incorrect : " + latestResponseMap.getStatusCode(), currentStatusCode.value(), is(statusCode));
}

@And("^the client receives server version (.+)$")
public void the_client_receives_server_version_body(String version) {
assertThat(latestResponse.getBody(), is(version));
}

@And("^response has (.+)$")
public void responseHas(String key) {
assertTrue("Key not found in response key='"+key+"'", latestResponseMap.getBody().containsKey(key));
}

@And("^response key (.+) equal to (.+)$")
public void responseValue(String key, String valueStr) {
Object valueExpected = resolveExpression(valueStr);
Object valueActual = valueExpected == null ? resolveExpression(key) : resolveExpression(key, valueExpected.getClass());
assertEquals("Value not equal for '"+key+"' which is '"+valueActual+"' != '"+valueExpected+"'",valueExpected, valueActual);
}

public Object resolveExpression(String expression) {
return resolveExpression(expression , null);
}

public Object resolveExpression(String expression, Class clazzRequired) {
SpelParserConfiguration config = new SpelParserConfiguration(true,true);
ExpressionParser parser = new SpelExpressionParser(config);
StandardEvaluationContext context = new StandardEvaluationContext(latestResponseMap);
context.addPropertyAccessor(new MapAccessor());
Expression exp = parser.parseExpression(expression);
if (clazzRequired != null) {
return exp.getValue(context, clazzRequired);
} else {
return exp.getValue(context);
}
}
}
8 changes: 8 additions & 0 deletions ui/web/src/test/resources/application.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
spring:
data:
mongodb.uri: mongodb://localhost:27017/galleryMineTest

javamelody:
# Enable JavaMelody auto-configuration (optional, default: true)
enabled: false

7 changes: 7 additions & 0 deletions ui/web/src/test/resources/folders.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Feature: Test Main Controller methods

Scenario: Test list Folders of gallery
When api call /listFolders
Then response status code is 200
And response key body.status equal to 200
And response key body.list.size equal to 10

0 comments on commit 46546a9

Please sign in to comment.