se.sundsvall.dept44.test.AbstractAppTest Maven / Gradle / Ivy
The newest version!
package se.sundsvall.dept44.test;
import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor;
import static com.github.tomakehurst.wiremock.matching.UrlPattern.fromOneOf;
import static java.lang.Class.forName;
import static java.lang.String.format;
import static java.nio.file.Files.readString;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static java.util.concurrent.TimeUnit.SECONDS;
import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals;
import static net.javacrumbs.jsonunit.JsonAssert.setOptions;
import static org.apache.commons.lang3.ObjectUtils.allNotNull;
import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.http.MediaType.APPLICATION_PROBLEM_JSON;
import static org.springframework.util.CollectionUtils.isEmpty;
import static org.springframework.util.ResourceUtils.getFile;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.Function;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ResourceUtils;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriBuilder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.VerificationException;
import com.github.tomakehurst.wiremock.common.ClasspathFileSource;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import com.github.tomakehurst.wiremock.extension.Extension;
import com.github.tomakehurst.wiremock.standalone.JsonFileMappingsSource;
import com.github.tomakehurst.wiremock.verification.LoggedRequest;
import net.javacrumbs.jsonunit.JsonAssert;
import net.javacrumbs.jsonunit.core.Option;
public abstract class AbstractAppTest {
private static final String FILES_DIR = "__files/";
private static final String COMMON_MAPPING_DIR = "/common";
private static final String MAPPING_DIRECTORY = "/mappings";
private static final ObjectMapper JSON_MAPPER = JsonMapper.builder().findAndAddModules().build();
private static final UriBuilder URI_BUILDER = new DefaultUriBuilderFactory().builder();
private static final int DEFAULT_VERIFICATION_DELAY_IN_SECONDS = 5;
private static final Class> DEFAULT_RESPONSE_TYPE = String.class;
private static final MediaType DEFAULT_CONTENT_TYPE = APPLICATION_JSON;
private final Logger logger = LoggerFactory.getLogger(getClass().getName());
private boolean expectedResponseBodyIsNull;
private int maxVerificationDelayInSeconds = DEFAULT_VERIFICATION_DELAY_IN_SECONDS;
private MultiValueMap multipartBody;
private String requestBody;
private String expectedResponseBody;
private byte[] expectedResponseBinary;
private Class> expectedResponseType = DEFAULT_RESPONSE_TYPE;
private String mappingPath;
private String servicePath;
private ResponseEntity> response;
private String responseBody;
private HttpHeaders responseHeaders;
private HttpMethod method;
private MediaType contentType = DEFAULT_CONTENT_TYPE;
private HttpStatus expectedResponseStatus;
private HttpHeaders expectedResponseHeaders;
private Map headerValues;
private String testDirectoryPath;
private String testCaseName;
@Autowired
protected TestRestTemplate restTemplate;
@Autowired
protected WireMockServer wiremock;
public AbstractAppTest reset() {
expectedResponseBodyIsNull = false;
multipartBody = null;
requestBody = null;
expectedResponseBody = null;
expectedResponseBinary = null;
expectedResponseType = DEFAULT_RESPONSE_TYPE;
mappingPath = null;
servicePath = null;
response = null;
responseBody = null;
responseHeaders = null;
method = null;
contentType = DEFAULT_CONTENT_TYPE;
expectedResponseStatus = null;
expectedResponseHeaders = null;
headerValues = null;
testDirectoryPath = null;
testCaseName = null;
return this;
}
public AbstractAppTest setupPaths() {
// Fetch test case name.
testCaseName = getTestMethodName();
mappingPath = wiremock.getOptions().filesRoot().getPath();
if (!mappingPath.endsWith("/")) {
mappingPath += "/";
}
testDirectoryPath = "classpath:" + mappingPath + FILES_DIR + getTestMethodName() + FileSystems.getDefault().getSeparator();
return this;
}
public AbstractAppTest setupCall() {
reset();
initializeJsonAssert();
setupPaths();
wiremock.loadMappingsUsing(new JsonFileMappingsSource(
new ClasspathFileSource(mappingPath + FILES_DIR + COMMON_MAPPING_DIR + MAPPING_DIRECTORY)));
if (nonNull(testCaseName)) {
wiremock.loadMappingsUsing(new JsonFileMappingsSource(
new ClasspathFileSource(mappingPath + FILES_DIR + testCaseName + MAPPING_DIRECTORY)));
}
return this;
}
public AbstractAppTest withExtensions(final Extension... extensions) {
((WireMockConfiguration) wiremock.getOptions()).extensions(extensions);
return this;
}
public AbstractAppTest withHttpMethod(final HttpMethod method) {
this.method = method;
return this;
}
public AbstractAppTest withContentType(final MediaType contentType) {
this.contentType = contentType;
return this;
}
public AbstractAppTest withExpectedResponseStatus(final HttpStatus status) {
this.expectedResponseStatus = status;
return this;
}
public AbstractAppTest withExpectedResponseType(final Class> type) {
this.expectedResponseType = type;
return this;
}
public AbstractAppTest withHeader(final String key, final String value) {
if (isNull(headerValues)) {
headerValues = new HashMap<>();
}
headerValues.put(key, value);
return this;
}
/**
* Set expected response header.
*
* @param expectedHeaderKey the expected header key.
* @param expectedHeaderValue the list of expected header values, as regular expressions.
* @return AbstractAppTest
*/
public AbstractAppTest withExpectedResponseHeader(final String expectedHeaderKey, final List expectedHeaderValue) {
if (isNull(expectedResponseHeaders)) {
expectedResponseHeaders = new HttpHeaders();
}
expectedResponseHeaders.put(expectedHeaderKey, expectedHeaderValue);
return this;
}
/**
* Method takes a JSON response string or a file name where the response can be
* read from.
*
* @param expectedResponse raw json response string or filename where the response can be
* read from
* @return AbstractAppTest
*/
public AbstractAppTest withExpectedResponse(final String expectedResponse) {
final var contentFromFile = fromTestFile(expectedResponse);
if (nonNull(contentFromFile)) {
expectedResponseBody = contentFromFile;
} else {
expectedResponseBody = expectedResponse;
}
return this;
}
/**
* Method takes a file name to a binary file.
*
* @param expectedResponseFile the filename where the binary response can be read from.
* @return AbstractAppTest
* @throws IOException if file can't be read.
*/
public AbstractAppTest withExpectedBinaryResponse(final String expectedResponseFile) throws IOException {
final var file = ResourceUtils.getFile(testDirectoryPath + expectedResponseFile);
expectedResponseBinary = Files.readAllBytes(file.toPath());
expectedResponseType = byte[].class;
return this;
}
public AbstractAppTest withExpectedResponseBodyIsNull() {
this.expectedResponseBodyIsNull = true;
return this;
}
public AbstractAppTest withServicePath(final String servicePath) {
this.servicePath = servicePath;
return this;
}
public AbstractAppTest withServicePath(final Function servicePathFunction) {
servicePath = servicePathFunction.apply(URI_BUILDER).toString();
return this;
}
/**
* Method adds options to be used when assertion of json is done, for example IGNORING_EXTRA_ARRAY_ITEMS.
* By default the test will compare arrays with option to ignore array order. If the need to use
* maximum strictness in JsonAssert - send in null or an empty list to just reset options to
* JsonAsserts default ones.
*
* @param options list of options to use when doing the json assertion or null/empty list for resetting
* to JsonAssert defaults (strict comparison)
* @return AbstractAppTest
*/
public AbstractAppTest withJsonAssertOptions(final List