Compare commits

...

32 Commits

Author SHA1 Message Date
Tom Hombergs
500ee0a04f ignored test because it cannot run on CI 2018-05-27 21:55:26 +02:00
Tom Hombergs
4d67dd44d0 fix 2018-05-27 21:10:15 +02:00
Tom Hombergs
a4d70d5835 Example application for structuring a Spring Boot app 2018-05-27 20:55:24 +02:00
Tom Hombergs
f2b83425ac inital checking for spring-boot-test example project 2018-05-20 20:38:28 +02:00
Tom Hombergs
f854262f80 changed package name 2018-03-18 22:44:54 +01:00
Tom Hombergs
11d70c0c12 pact feign consumer example 2018-03-17 12:58:11 +01:00
Tom Hombergs
4e26aa589e moved deprecated modules 2018-03-17 12:04:05 +01:00
Tom Hombergs
5d76ba1375 added example for modularizing a Spring Boot application 2018-02-01 23:32:01 +01:00
Tom Hombergs
1d50d8f513 ignoring offline cdc test 2018-01-18 22:48:20 +01:00
Tom Hombergs
0e4b84b72e polishing spring-cloud-contract consumer example 2018-01-18 22:19:42 +01:00
Tom Hombergs
60de5fafac Merge remote-tracking branch 'origin/master' 2018-01-17 21:45:54 +01:00
Tom Hombergs
21b2bccbad added spring cloud contract consumer example 2018-01-17 21:45:43 +01:00
Tom Hombergs
f4a0addd24 Update README.md 2018-01-15 16:40:22 +01:00
Tom Hombergs
a5e7696775 added pact contract 2018-01-13 12:53:21 +01:00
Tom Hombergs
d68a99e92b polishing and readme 2018-01-10 00:09:05 +01:00
Tom Hombergs
3a6149b190 added links to blog posts 2018-01-08 22:51:48 +01:00
Tom Hombergs
2c775cdaa2 refactored gradle project structure 2018-01-08 22:32:55 +01:00
Tom Hombergs
387d9a5711 Merge pull request #5 from thombergs/spring-cloud-contract
added example with spring cloud contract
2018-01-08 22:05:48 +01:00
Tom Hombergs
99f6e3fb71 added example with spring cloud contract 2018-01-08 20:23:04 +01:00
Tom Hombergs
7bce17b055 upgraded pact-web and pact-jvm-provider-spring 2018-01-02 23:01:38 +01:00
Tom Hombergs
c9ae2ca600 updated pact between Angular app and Spring provider 2017-12-31 15:25:57 +01:00
Tom Hombergs
5f46041ccd updated gradle 2017-12-31 15:25:06 +01:00
Tom Hombergs
d139eecb2d updated angular-pact example 2017-12-10 20:24:55 +01:00
Tom Hombergs
671f5e501e moved angular-pact to pact-angular 2017-12-09 13:30:06 +01:00
Tom Hombergs
a885f98f2f fixed dependency to pact-node 2017-11-13 23:17:08 +01:00
Tom Hombergs
f10c8d2bfc added angular pact example 2017-11-06 23:16:47 +01:00
Tom Hombergs
b93c05fbe5 fixed unit test 2017-10-18 21:01:54 +02:00
Tom Hombergs
8e69a627cf Merge pull request #3 from thombergs/angular-pact
Angular pact
2017-10-18 20:32:07 +02:00
Tom Hombergs
c8af61e065 Merge remote-tracking branch 'origin/master' 2017-10-10 23:14:27 +02:00
Tom Hombergs
d1c5091ee8 added junit examples 2017-10-10 23:14:19 +02:00
Tom Hombergs
83b7eb429e Merge pull request #1 from snicoll/patch-1
Remove unnecessary EnableAutoConfiguration
2017-09-19 20:23:18 +02:00
Stéphane Nicoll
3e866fbe44 Remove unnecessary EnableAutoConfiguration 2017-09-19 08:41:04 +02:00
308 changed files with 4869 additions and 752 deletions

View File

@@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: org.projectlombok:lombok:1.16.20">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.16.20/ac76d9b956045631d1561a09289cbf472e077c01/lombok-1.16.20.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.16.20/69ebf81bb97bdb3c9581c171762bb4929cb5289c/lombok-1.16.20-sources.jar!/" />
</SOURCES>
</library>
</component>

13
.idea/modules/spring-boot-testing.iml generated Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="spring-boot-testing" external.linked.project.path="$MODULE_DIR$/../../spring-boot/spring-boot-testing" external.root.project.path="$MODULE_DIR$/../../spring-boot/spring-boot-testing" external.system.id="GRADLE" external.system.module.group="reflectoring.io" external.system.module.version="0.0.1-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/../../spring-boot/spring-boot-testing">
<excludeFolder url="file://$MODULE_DIR$/../../spring-boot/spring-boot-testing/.gradle" />
<excludeFolder url="file://$MODULE_DIR$/../../spring-boot/spring-boot-testing/build" />
<excludeFolder url="file://$MODULE_DIR$/../../spring-boot/spring-boot-testing/out" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.2-bin.zip

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.2-bin.zip

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.2-all.zip

View File

@@ -0,0 +1,3 @@
# Examples with JUnit 4 and JUnit 5
Have a look at [the code](/src/test/java/com/example/demo/)

View File

@@ -0,0 +1,25 @@
buildscript {
repositories {
mavenCentral()
}
}
apply plugin: 'java'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenLocal()
mavenCentral()
}
ext {
springCloudVersion = 'Dalston.SR2'
}
dependencies {
testCompile 'org.junit.jupiter:junit-jupiter-engine:5.0.1'
testCompile 'junit:junit:4.12'
}

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.2-bin.zip

View File

@@ -0,0 +1,15 @@
package com.example.demo.connectionchecking;
public class ConnectionChecker {
private String uri;
public ConnectionChecker(String uri) {
this.uri = uri;
}
public boolean connect() {
return false;
}
}

View File

@@ -0,0 +1,31 @@
package com.example.demo.connectionchecking.junit4;
import com.example.demo.connectionchecking.ConnectionChecker;
import org.junit.AssumptionViolatedException;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
public class AssumingConnection implements TestRule {
private ConnectionChecker checker;
public AssumingConnection(ConnectionChecker checker) {
this.checker = checker;
}
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
if (!checker.connect()) {
throw new AssumptionViolatedException("Could not connect. Skipping test!");
} else {
base.evaluate();
}
}
};
}
}

View File

@@ -0,0 +1,19 @@
package com.example.demo.connectionchecking.junit4;
import com.example.demo.connectionchecking.ConnectionChecker;
import org.junit.ClassRule;
import org.junit.Test;
import static org.junit.Assert.fail;
public class ConnectionCheckingJunit4Test {
@ClassRule
public static AssumingConnection assumingConnection = new AssumingConnection(new ConnectionChecker("http://my.integration.system"));
@Test
public void testOnlyWhenConnected() {
fail("Booh!");
}
}

View File

@@ -0,0 +1,14 @@
package com.example.demo.connectionchecking.junit5;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.junit.jupiter.api.extension.ExtendWith;
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(AssumeConnectionCondition.class)
public @interface AssumeConnection {
String uri();
}

View File

@@ -0,0 +1,28 @@
package com.example.demo.connectionchecking.junit5;
import java.util.Optional;
import com.example.demo.connectionchecking.ConnectionChecker;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
import static org.junit.platform.commons.util.AnnotationUtils.findAnnotation;
public class AssumeConnectionCondition implements ExecutionCondition {
@Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
Optional<AssumeConnection> annotation = findAnnotation(context.getElement(), AssumeConnection.class);
if (annotation.isPresent()) {
String uri = annotation.get().uri();
ConnectionChecker checker = new ConnectionChecker(uri);
if (!checker.connect()) {
return ConditionEvaluationResult.disabled(String.format("Could not connect to '%s'. Skipping test!", uri));
} else {
return ConditionEvaluationResult.enabled(String.format("Successfully connected to '%s'. Continuing test!", uri));
}
}
return ConditionEvaluationResult.enabled("No AssumeConnection annotation found. Continuing test.");
}
}

View File

@@ -0,0 +1,14 @@
package com.example.demo.connectionchecking.junit5;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.fail;
@AssumeConnection(uri = "http://my.integration.system")
public class ConnectionCheckingJunit5Test {
@Test
public void testOnlyWhenConnected() {
fail("Booh!");
}
}

View File

@@ -0,0 +1,120 @@
{
"provider": {
"name": "customerServiceProvider"
},
"consumer": {
"name": "addressClient"
},
"interactions": [
{
"description": "a request to the address collection resource",
"request": {
"method": "GET",
"path": "/addresses/"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/hal+json"
},
"body": {
"_embedded": {
"addresses": [
{
"street": "Elm Street",
"_links": {
"self": {
"href": "http://localhost:8080/addresses/1"
},
"address": {
"href": "http://localhost:8080/addresses/1"
},
"customer": {
"href": "http://localhost:8080/addresses/1/customer"
}
}
},
{
"street": "High Street",
"_links": {
"self": {
"href": "http://localhost:8080/addresses/2"
},
"address": {
"href": "http://localhost:8080/addresses/2"
},
"customer": {
"href": "http://localhost:8080/addresses/2/customer"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/addresses{?page,size,sort}",
"templated": true
},
"profile": {
"href": "http://localhost:8080/profile/addresses"
},
"search": {
"href": "http://localhost:8080/addresses/search"
}
},
"page": {
"size": 20,
"totalElements": 2,
"totalPages": 1,
"number": 0
}
}
},
"providerStates": [
{
"name": "a collection of 2 addresses"
}
]
},
{
"description": "a request to the address resource",
"request": {
"method": "GET",
"path": "/addresses/1"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/hal+json"
},
"body": {
"street": "Elm Street",
"_links": {
"self": {
"href": "http://localhost:8080/addresses/1"
},
"address": {
"href": "http://localhost:8080/addresses/1"
},
"customer": {
"href": "http://localhost:8080/addresses/1/customer"
}
}
}
},
"providerStates": [
{
"name": "a single address"
}
]
}
],
"metadata": {
"pact-specification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.5.2"
}
}
}

View File

@@ -18,3 +18,13 @@ a consumer against the Pact.
Run `npm install` to load the needed javascript libraries and then `npm run test` to Run `npm install` to load the needed javascript libraries and then `npm run test` to
run the tests. After the tests have successfully run the created pact file will be run the tests. After the tests have successfully run the created pact file will be
created in the folder `pacts`. created in the folder `pacts`.
Then, you can call `npm run publish-pacts` to publish the pact files to a [Pact Broker](https://github.com/pact-foundation/pact_broker).
You must set the following npm configs for the `publish-pacts` script to work:
```
npm config set angular-pact:brokerUrl <URL>
npm config set angular-pact:brokerUsername <USER>
npm config set angular-pact:brokerPassword <PASS>
```

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,8 @@
"build": "ng build", "build": "ng build",
"test": "cross-env LOGLEVEL=DEBUG ng test", "test": "cross-env LOGLEVEL=DEBUG ng test",
"lint": "ng lint", "lint": "ng lint",
"e2e": "ng e2e" "e2e": "ng e2e",
"publish-pacts": "node publish-pacts.js"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
@@ -45,9 +46,9 @@
"ts-node": "~3.2.0", "ts-node": "~3.2.0",
"tslint": "~5.7.0", "tslint": "~5.7.0",
"typescript": "~2.3.3", "typescript": "~2.3.3",
"@pact-foundation/pact-node": "~4.12", "@pact-foundation/pact-node": "6.5.0",
"@pact-foundation/karma-pact": "~2.1.0", "@pact-foundation/karma-pact": "2.1.3",
"pact-web": "~3.0", "@pact-foundation/pact-web": "5.3.0",
"cross-env": "^5.0.5" "cross-env": "^5.0.5"
} }
} }

View File

@@ -0,0 +1,78 @@
{
"consumer": {
"name": "ui"
},
"provider": {
"name": "userservice"
},
"interactions": [
{
"description": "a request to POST a person",
"providerState": "provider accepts a new person",
"request": {
"method": "POST",
"path": "/user-service/users",
"headers": {
"Content-Type": "application/json"
},
"body": {
"firstName": "Arthur",
"lastName": "Dent"
}
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"body": {
"id": 42
},
"matchingRules": {
"$.body": {
"match": "type"
}
}
}
},
{
"description": "a request to PUT a person",
"providerState": "person 42 exists",
"request": {
"method": "PUT",
"path": "/user-service/users/42",
"headers": {
"Content-Type": "application/json"
},
"body": {
"firstName": "Zaphod",
"lastName": "Beeblebrox"
},
"matchingRules": {
"$.body": {
"match": "type"
}
}
},
"response": {
"status": 200,
"headers": {
},
"body": {
"firstName": "Zaphod",
"lastName": "Beeblebrox"
},
"matchingRules": {
"$.body": {
"match": "type"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "2.0.0"
}
}
}

View File

@@ -0,0 +1,20 @@
let projectFolder = __dirname;
let pact = require('@pact-foundation/pact-node');
let project = require('./package.json');
let pactBrokerUrl = process.env.npm_package_config_brokerUrl;
let pactBrokerUsername = process.env.npm_package_config_brokerUsername;
let pactBrokerPassword = process.env.npm_package_config_brokerPassword;
let options = {
pactFilesOrDirs: [projectFolder + '/pacts'],
pactBroker: pactBrokerUrl,
consumerVersion: project.version,
tags: ['latest'],
pactBrokerUsername: pactBrokerUsername,
pactBrokerPassword: pactBrokerPassword
};
pact.publishPacts(options).then(function () {
console.log("Pacts successfully published!");
});

View File

@@ -2,20 +2,18 @@ import {TestBed} from '@angular/core/testing';
import {HttpClientModule} from '@angular/common/http'; import {HttpClientModule} from '@angular/common/http';
import {UserService} from './user.service'; import {UserService} from './user.service';
import {User} from './user'; import {User} from './user';
import * as Pact from 'pact-web'; import {PactWeb, Matchers} from '@pact-foundation/pact-web';
describe('UserService', () => { describe('UserService', () => {
let provider; let provider;
beforeAll(function (done) { beforeAll(function (done) {
provider = Pact({ provider = new PactWeb({
consumer: 'ui', consumer: 'ui',
provider: 'userservice', provider: 'userservice',
web: true,
port: 1234, port: 1234,
host: '127.0.0.1', host: '127.0.0.1',
logLevel: 'DEBUG'
}); });
// required for slower CI environments // required for slower CI environments
@@ -72,7 +70,7 @@ describe('UserService', () => {
}, },
willRespondWith: { willRespondWith: {
status: 201, status: 201,
body: Pact.Matchers.somethingLike({ body: Matchers.somethingLike({
id: createdUserId id: createdUserId
}), }),
headers: { headers: {
@@ -94,4 +92,41 @@ describe('UserService', () => {
}); });
describe('update()', () => {
const expectedUser: User = {
firstName: 'Zaphod',
lastName: 'Beeblebrox'
};
beforeAll((done) => {
provider.addInteraction({
state: `person 42 exists`,
uponReceiving: 'a request to PUT a person',
withRequest: {
method: 'PUT',
path: '/user-service/users/42',
body: Matchers.somethingLike(expectedUser),
headers: {
'Content-Type': 'application/json'
}
},
willRespondWith: {
status: 200,
body: Matchers.somethingLike(expectedUser)
}
}).then(done, error => done.fail(error));
});
it('should update a Person', (done) => {
const userService: UserService = TestBed.get(UserService);
userService.update(expectedUser, 42).subscribe(response => {
done();
}, error => {
done.fail(error);
});
});
});
}); });

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1,13 @@
# Testing a Spring Boot REST API Consumer against a Contract with Spring Cloud Contract
## Companion Blog Article
Read the [companion blog article](https://reflectoring.io/consumer-driven-contract-consumer-spring-cloud-contract/) to this repository.
## Getting Started
* have a look at the [contract](/src/test/resources/contracts)
* have a look at the [feign client](/src/main/java/io/reflectoring/UserClient.java)
* have a look at the [consumer test](/src/test/java/io/reflectoring/UserClientTest.java)
* run `./gradlew publishToMavenLocal` in the [producer project](../spring-cloud-contract-provider)
to create a provider stubs
* run `./gradlew build` in this project to verify the feign client against the stub

View File

@@ -0,0 +1,5 @@
userservice:
ribbon:
eureka:
enabled: false
listOfServers: localhost:8080

View File

@@ -0,0 +1,27 @@
apply plugin: 'org.springframework.boot'
buildscript {
repositories {
mavenLocal()
jcenter()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springboot_version}"
}
}
repositories {
mavenLocal()
jcenter()
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-data-jpa:${springboot_version}")
compile("org.springframework.boot:spring-boot-starter-web:${springboot_version}")
compile("org.springframework.cloud:spring-cloud-starter-feign:1.4.1.RELEASE")
compile('com.h2database:h2:1.4.196')
testCompile('org.codehaus.groovy:groovy-all:2.4.6')
testCompile("au.com.dius:pact-jvm-consumer-junit_2.11:3.5.2")
testCompile("org.springframework.boot:spring-boot-starter-test:${springboot_version}")
}

View File

@@ -0,0 +1,2 @@
springboot_version=1.5.9.RELEASE
verifier_version=1.2.2.RELEASE

Some files were not shown because too many files have changed in this diff Show More