[#89] Initial approach to the integration tests

- Fixed the tests
- Updated surefire
- Added integration tests (for the moment ignored)
- Fixed wrong surefire setup
This commit is contained in:
Marcin Grzejszczak
2016-01-08 19:08:34 +01:00
parent 7c54ad9e66
commit 9a8dc17d8c
29 changed files with 661 additions and 138 deletions

View File

@@ -18,6 +18,7 @@
<modules>
<module>spring-cloud-sleuth-sample</module>
<module>spring-cloud-sleuth-sample-test-core</module>
<module>spring-cloud-sleuth-sample-messaging</module>
<module>spring-cloud-sleuth-sample-ribbon</module>
<module>spring-cloud-sleuth-sample-zipkin</module>
@@ -26,17 +27,27 @@
</modules>
<build>
<pluginManagement>
<plugins>
<plugin>
<!--skip deploy (this is just a test module) -->
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<pluginManagement>
<plugins>
<plugin>
<!--skip deploy (this is just a test module) -->
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-sample-test-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -39,6 +39,10 @@
</plugins>
</build>
<properties>
<testcontainers.jackson.version>2.1.0</testcontainers.jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -73,6 +77,21 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-sample-test-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -16,15 +16,14 @@
package sample;
import java.util.Random;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.Random;
/**
* @author Spencer Gibb
*/

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="org.springframework.cloud.sleuth" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</configuration>

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package integration;
import com.github.kristofa.brave.SpanCollector;
import com.twitter.zipkin.gen.BinaryAnnotation;
import com.twitter.zipkin.gen.Span;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.WebIntegrationTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.JdkIdGenerator;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.testcontainers.containers.DockerComposeContainer;
import sample.SampleMessagingApplication;
import tools.*;
import java.io.File;
import java.util.Collection;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { MessagingApplicationDockerTests.Config.class, SampleMessagingApplication.class })
@WebIntegrationTest
@Slf4j
@Ignore("Ignored until working on profile")
public class MessagingApplicationDockerTests extends AbstractIntegrationTest {
private static int port = 3381;
private static String sampleAppUrl = "http://localhost:" + port;
RestTemplate restTemplate = new AssertingRestTemplate();
@Autowired IntegrationTestSpanCollector integrationTestSpanCollector;
@ClassRule
public static DockerComposeContainer environment =
new DockerComposeContainer(new File("src/test/resources/docker-compose.yml"))
.withExposedService("rabbitmq_1", 5672);
@After
public void cleanup() {
integrationTestSpanCollector.hashedSpans.clear();
}
@Test
public void should_propagate_spans_for_messaging() {
String traceId = new JdkIdGenerator().generateId().toString();
await().until(httpMessageWithTraceIdInHeadersIsSuccessfullySent(sampleAppUrl + "/", traceId));
await().until(() -> {
thenAllSpansHaveTraceIdEqualTo(traceId);
});
}
@Test
public void should_propagate_spans_for_messaging_with_async() {
String traceId = new JdkIdGenerator().generateId().toString();
await().until(httpMessageWithTraceIdInHeadersIsSuccessfullySent(sampleAppUrl + "/xform", traceId));
await().until(() -> {
thenAllSpansHaveTraceIdEqualTo(traceId);
thenThereIsAtLeastOneBinaryAnnotationWithKey("background-sleep-millis");
});
}
private void thenThereIsAtLeastOneBinaryAnnotationWithKey(String binaryAnnotationKey) {
then(integrationTestSpanCollector.hashedSpans.stream()
.filter(Span::isSetBinary_annotations)
.map(Span::getBinary_annotations)
.flatMap(Collection::stream)
.filter(binaryAnnotation -> StringUtils.hasText(binaryAnnotation.getKey()))
.map(BinaryAnnotation::getKey)
.anyMatch(binaryAnnotationKey::equals)).isTrue();
}
private RequestSendingRunnable httpMessageWithTraceIdInHeadersIsSuccessfullySent(String endpoint, String traceId) {
return new RequestSendingRunnable(restTemplate, endpoint, traceId);
}
private void thenAllSpansHaveTraceIdEqualTo(String traceId) {
then(integrationTestSpanCollector.hashedSpans.stream().allMatch(span -> span.getTrace_id() == zipkinHashedTraceId(traceId))).isTrue();
}
@Configuration
static class Config {
@Bean SpanCollector integrationTestSpanCollector() {
return new IntegrationTestSpanCollector();
}
}
}

View File

@@ -0,0 +1,5 @@
rabbitmq:
image: rabbitmq:management
ports:
- 5672:5672
- 15672:15672

View File

@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-sleuth-sample-test-core</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud Sleuth Sample Test Core</name>
<description>Test classes for Spring Cloud Samples</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-samples</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<build>
<pluginManagement>
<plugins>
<plugin>
<!--skip deploy (this is just a test module) -->
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<properties>
<testcontainers.jackson.version>2.1.0</testcontainers.jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.jayway.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>docker-compose</artifactId>
<version>0.9.8</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${testcontainers.jackson.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${testcontainers.jackson.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${testcontainers.jackson.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package tools;
import com.jayway.awaitility.Awaitility;
import com.jayway.awaitility.core.ConditionFactory;
import org.junit.experimental.categories.Category;
import org.springframework.beans.factory.annotation.Value;
import static java.util.concurrent.TimeUnit.SECONDS;
/**
* @author Marcin Grzejszczak
*/
@Category(DockerTests.class)
abstract public class AbstractIntegrationTest {
@Value("${test.pollinterval:1}") protected int pollInterval;
@Value("${test.timeout:1}") protected int timeout;
protected ConditionFactory await() {
return Awaitility.await().pollInterval(pollInterval, SECONDS).atMost(timeout, SECONDS);
}
protected long zipkinHashedTraceId(String string) {
long h = 1125899906842597L;
if (string == null) {
return h;
}
int len = string.length();
for (int i = 0; i < len; i++) {
h = 31 * h + string.charAt(i);
}
return h;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package tools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.*;
import java.io.IOException;
import java.net.URI;
/**
*
* RestTemplate that logs erroneous responses and throws AssertionsError on any connection issues
*
* @author Marcin Grzejszczak
*/
@Slf4j
public class AssertingRestTemplate extends RestTemplate {
public AssertingRestTemplate() {
setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
if (hasError(response)) {
log.error("Response has status code [" + response.getStatusCode() + "] and text [" + response.getStatusText() + "])");
}
}
});
}
@Override
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor) throws RestClientException {
try {
return super.doExecute(url, method, requestCallback, responseExtractor);
} catch (Exception e) {
log.error("Exception occurred while sending the message", e);
throw new AssertionError(e);
}
}
}

View File

@@ -0,0 +1,7 @@
package tools;
/**
* @author Marcin Grzejszczak
*/
interface DockerTests {
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package tools;
import com.github.kristofa.brave.LoggingSpanCollector;
import com.twitter.zipkin.gen.Span;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* Span Collector that logs spans and adds Spans to a list
*
* @author Marcin Grzejszczak
*/
public class IntegrationTestSpanCollector extends LoggingSpanCollector {
public List<Span> hashedSpans = Collections.<Span>synchronizedList(new LinkedList<Span>());
@Override
public void collect(Span span) {
super.collect(span);
hashedSpans.add(span);
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package tools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import static org.assertj.core.api.BDDAssertions.then;
/**
* Runnable that will send a request via the provide rest template to the
* given url. It will also append the provided TraceID as the request's header
*
* @author Marcin Grzejszczak
*/
@Slf4j
public class RequestSendingRunnable implements Runnable {
private final RestTemplate restTemplate;
private final String url;
private final String traceId;
public RequestSendingRunnable(RestTemplate restTemplate, String url, String traceId) {
this.restTemplate = restTemplate;
this.url = url;
this.traceId = traceId;
}
@Override
public void run() {
ResponseEntity<String> responseEntity = restTemplate.exchange(requestWithTraceId(traceId), String.class);
then(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
log.info("Received the following response [{}]", responseEntity);
}
private RequestEntity requestWithTraceId(String traceId) {
HttpHeaders headers = new HttpHeaders();
headers.add(Trace.TRACE_ID_NAME, traceId);
URI uri = URI.create(url);
RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, uri);
log.info("Request [" + requestEntity + "] is ready");
return requestEntity;
}
}