Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Talk Creating and testing REST and Messaging contracts with Spring Cloud Contract

Talk Creating and testing REST and Messaging contracts with Spring Cloud Contract

REST and Messaging solutions do not come with an in-built contract compliance mechanism, which in many ways is a great thing. However, while working with microservice-based systems, it often appears that a practical mechanism that would provide help in shaping and describing REST and Messaging contracts would come in handy. Similarly, creating integration and acceptance tests in such systems presents many challenges.

In this talk, I present Spring Cloud Contract, a contract verification solution, written in big part in Groovy, that allows for both: easily shaping REST and Messaging contracts and verifying if our app adheres to them using automatically generated Spock tests. I will show how, using Spring Cloud Contract, we can quickly generate automatically-tested stubs from simple Groovy DSL scripts. I will talk about the typical usages and script examples, as well as possible problems and ways of handling them.

Olga Maciaszek-Sharma

July 27, 2018
Tweet

More Decks by Olga Maciaszek-Sharma

Other Decks in Technology

Transcript

  1. Creating and testing REST
    Creating and testing REST
    and Messaging contracts
    and Messaging contracts
    with Spring Cloud Contract
    with Spring Cloud Contract
    @olga_maciaszek
    Olga Maciaszek-Sharma
    27.07.2018

    View full-size slide

  2. Olga Maciaszek-Sharma
    Olga Maciaszek-Sharma
    @olga_maciaszek
    https:/
    /github.com/OlgaMaciaszek
    https:/
    /github.com/OlgaMaciaszek
    @
    @olga_maciaszek
    olga_maciaszek

    View full-size slide

  3. E2E Tests
    E2E Tests
    @olga_maciaszek

    View full-size slide

  4. @olga_maciaszek
    Source: https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html
    Test Pyramid
    Test Pyramid

    View full-size slide

  5. Microservices
    Microservices
    @olga_maciaszek

    View full-size slide

  6. Fully Deployed Test
    Fully Deployed Test
    Environment
    Environment
    @olga_maciaszek
    Environment per Service
    Environment per Service
    One To Rule Them All
    One To Rule Them All

    View full-size slide

  7. Deployment
    Deployment
    Coupling
    Coupling
    @olga_maciaszek

    View full-size slide

  8. Failsafe
    Failsafe
    vs.
    vs.
    Safe To Fail
    Safe To Fail
    @olga_maciaszek

    View full-size slide

  9. Circuit Breakers
    Circuit Breakers
    @olga_maciaszek

    View full-size slide

  10. How many E2E?
    How many E2E?
    @olga_maciaszek

    View full-size slide

  11. Stubbed Environments
    Stubbed Environments
    @olga_maciaszek
    Outdated Stubs
    Outdated Stubs

    View full-size slide

  12. Contracts
    Contracts
    @olga_maciaszek
    Monitoring
    Monitoring
    Alerting
    Alerting
    Logging
    Logging
    Unit Tests
    Unit Tests

    View full-size slide

  13. Input A ==> Output B
    Input A ==> Output B
    @olga_maciaszek
    Contracts
    Contracts

    View full-size slide

  14. Consumer-Driven Contracts
    Consumer-Driven Contracts
    https://martinfowler.com/articles/consum
    erDrivenContracts.html
    @olga_maciaszek

    View full-size slide

  15. Contract Tests
    Contract Tests
    https://martinfowler.com/bliki/ContractTest.html
    @olga_maciaszek

    View full-size slide

  16. Spring Cloud Contract
    Spring Cloud Contract
    https://cloud.spring.io/spring-cloud-contract/
    @olga_maciaszek

    View full-size slide

  17. SCC Flow
    SCC Flow
    @olga_maciaszek

    View full-size slide

  18. SCC Flow
    SCC Flow
    @olga_maciaszek

    View full-size slide

  19. Contracts
    Contracts
    package contracts
    org.springframework.cloud.contract.spec.Contract.make {
    request {
    method 'PUT'
    url '/fraudcheck'
    body([
    "client.id": $(regex('[0-9]{10}')),
    loanAmount: 99999
    ])
    headers {
    contentType('application/json')
    }
    }
    response {
    status OK()
    body([
    fraudCheckStatus: "FRAUD",
    "rejection.reason": "Amount too high"
    ])
    headers {
    contentType('application/json')
    }
    }
    }
    @olga_maciaszek

    View full-size slide

  20. Contracts
    Contracts
    request:
    method: PUT
    url: /fraudcheck
    body:
    "client.id": 1234567890
    loanAmount: 99999
    headers:
    Content-Type: application/json
    matchers:
    body:
    - path: $.['client.id']
    type: by_regex
    value: "[0-9]{10}"
    response:
    status: 200
    body:
    fraudCheckStatus: "FRAUD"
    "rejection.reason": "Amount too high"
    headers:
    Content-Type: application/json;charset=UTF-8
    @olga_maciaszek

    View full-size slide

  21. SCC Flow
    SCC Flow
    @olga_maciaszek

    View full-size slide

  22. Gradle Setup - Producer
    Gradle Setup - Producer
    buildscript {
    repositories {
    mavenCentral()
    maven {url 'http://repo.spring.io/release'}
    }
    dependencies {
    classpath("org.springframework.cloud:spring-cloud-contract-gradle
    }
    }
    apply plugin: 'spring-cloud-contract'
    contracts {
    targetFramework = 'SPOCK'
    baseClassForTests = 'com.example.contractsdemo.FraudCheckTestBaseClass'
    }
    @olga_maciaszek

    View full-size slide

  23. Contract Tests
    Contract Tests
    def validate_shouldMarkClientAsFraud() {
    given:
    def request = given()
    .header('Content-Type', 'application/vnd.fraud.v1+json')
    .body('{"clientId":"12345678902","loanAmount":99999}')
    when:
    def response = given().spec(request).put("/fraudcheck")
    then:
    response.statusCode == 200
    response.header('Content-Type') == 'application/vnd.fraud.v1+json'
    and:
    DocumentContext parsedJson = JsonPath.parse(response.body.asString())
    assertTassertThatJson(parsedJson).field("rejectionReason")
    .isEqualTo("Amount too high")
    assertThatJson(parsedJson).field("fraudCheckStatus").isEqualTo("FRAUD")
    }
    @olga_maciaszek

    View full-size slide

  24. Stubs
    Stubs
    {
    "request": {
    "url": "/fraudcheck",
    "method": "PUT",
    "bodyPatterns": [
    {
    "matchesJsonPath": "$[?(@.loanAmount == 99999)]"
    },
    {
    "matchesJsonPath": "$[?(@.clientId == '12345678902')]"
    }
    ],
    "headers": {
    "Content-Type": {
    "equalTo": "application/vnd.fraud.v1+json"
    }
    }
    },
    "response": {
    "status": 200,
    "body": "{\"fraudCheckStatus\":\"FRAUD\",
    \"rejectionReason\":\"Amount too high\"}",
    "headers": {
    "Content-Type": "application/vnd.fraud.v1+json"
    }
    }
    }
    @olga_maciaszek

    View full-size slide

  25. SCC Flow
    SCC Flow
    @olga_maciaszek

    View full-size slide

  26. SCC Flow
    SCC Flow
    @olga_maciaszek

    View full-size slide

  27. Gradle Setup - Consumer
    Gradle Setup - Consumer
    dependencies {
    testCompile('org.springframework.cloud:spring-cloud-starter-contract-stub-runner')
    }
    @olga_maciaszek

    View full-size slide

  28. Using Stubs
    Using Stubs
    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment=WebEnvironment.NONE)
    @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
    stubsMode = StubRunnerProperties.StubsMode.LOCAL)
    @DirtiesContext
    public class LoanApplicationServiceTests { ... }
    @olga_maciaszek
    @StubRunnerPort

    View full-size slide

  29. Using Stubs
    Using Stubs
    stubrunner:
    repositoryRoot: classpath:m2repo/repository/
    ids:
    - org.springframework.cloud.contract.verifier.stubs:loanIssuance
    - org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer
    - org.springframework.cloud.contract.verifier.stubs:bootService
    stubs-mode: remote
    @olga_maciaszek
    @ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
    @SpringBootTest(properties = [" stubrunner.cloud.enabled=false"])
    @AutoConfigureStubRunner
    @DirtiesContext
    @ActiveProfiles("test")
    class StubRunnerConfigurationSpec extends Specification {
    @Autowired StubFinder stubFinder
    def 'should start WireMock servers'() {
    expect: 'WireMocks are running'
    stubFinder.findStubUrl('org.verifier.stubs',
    'loanIssuance') != null }

    View full-size slide

  30. Using Stubs
    Using Stubs
    @Rule public StubRunnerRule rule = new StubRunnerRule()
    .downloadStub("com.example","fraud-service")
    .stubsMode(StubRunnerProperties.StubsMode.LOCAL)
    .withStubPerConsumer(true)
    .withConsumerName("loan-service");
    int fraudServicePort = rule.findStubUrl("fraud-service").getPort()
    @olga_maciaszek

    View full-size slide

  31. Code Demo
    Code Demo
    @olga_maciaszek

    View full-size slide

  32. Messaging Contracts
    Messaging Contracts
    def contractDsl = Contract.make {
    label 'some_label'
    input {
    messageFrom('inputQueue')
    messageBody([
    bookName: 'foo'
    ])
    messageHeaders {
    header('sample', 'header')
    }
    }
    outputMessage {
    sentTo('outputQueue')
    body([
    bookName: 'foo'
    ])
    headers {
    header('BOOK-NAME', 'foo')
    }
    }
    }
    @olga_maciaszek

    View full-size slide

  33. Messaging Contract Tests
    Messaging Contract Tests
    given:
    ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
    '''{"bookName":"foo"}''',
    ['sample': 'header']
    )
    when:
    contractVerifierMessaging.send(inputMessage, 'inputQueue')
    then:
    ContractVerifierMessage response = contractVerifierMessaging
    .receive('outputQueue')
    assert response != null
    response.getHeader('BOOK-NAME')?.toString() == 'foo'
    and:
    DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper
    .writeValueAsString(response.payload))
    assertThatJson(parsedJson).field("bookName").isEqualTo("foo")
    @olga_maciaszek

    View full-size slide

  34. Messaging Client-Side Test
    Messaging Client-Side Test
    given:
    messaging.send(new BookReturned('foo'), [sample: 'header'], 'input')
    when:
    Message> receivedMessage = messaging.receive('outputTest')
    then:
    receivedMessage != null
    assertJsons(receivedMessage.payload)
    receivedMessage.headers.get('BOOK-NAME') == 'foo'
    @olga_maciaszek

    View full-size slide

  35. Public API
    Public API
    Spring Rest Docs
    Spring Rest Docs
    MockMvc Tests -> REST Docs, Stubs, Contracts
    @olga_maciaszek

    View full-size slide

  36. Public API
    Public API
    mvn spring-cloud-contract:run -Dstubs="org.springframework:gs-rest-service"
    Docs
    @olga_maciaszek

    View full-size slide

  37. Generate Rest Docs
    Generate Rest Docs
    from Contracts
    from Contracts
    @olga_maciaszek

    View full-size slide

  38. Contracts Scope
    Contracts Scope
    Skip Contracts for non-
    Skip Contracts for non-
    essential functionality
    essential functionality
    @olga_maciaszek

    View full-size slide

  39. Contracts Scope
    Contracts Scope
    Skip multiple values
    Skip multiple values
    @olga_maciaszek

    View full-size slide

  40. Skip Multiple Values
    Skip Multiple Values
    @olga_maciaszek
    request {
    method 'PUT'
    url '/fraudcheck'
    body([
    "client.id": $(regex('[0-9]{10}')),
    "client.type" : "INDIVIDUAL"
    loanAmount: 99999
    ])
    headers {
    contentType('application/json')
    }}
    request {
    method 'PUT'
    url '/fraudcheck'
    body([
    "client.id": $(regex('[0-9]{10}')),
    "client.type" : "BUSINESS"
    loanAmount: 99999
    ])
    headers {
    contentType('application/json')
    }}

    View full-size slide

  41. Architecture TDD
    Architecture TDD
    @olga_maciaszek

    View full-size slide

  42. Backwards
    Backwards
    Compatibility Check
    Compatibility Check
    Spring Cloud Pipelines
    Spring Cloud Pipelines
    @olga_maciaszek

    View full-size slide

  43. Deferred Updates
    Deferred Updates
    @olga_maciaszek

    View full-size slide

  44. No app versioning
    No app versioning
    @olga_maciaszek

    View full-size slide

  45. Spring Cloud Contract at
    Spring Cloud Contract at
    Devskiller - case study
    Devskiller - case study
    blogpost
    blogpost
    @olga_maciaszek

    View full-size slide

  46. Demo Code Links
    Demo Code Links
    https://github.com/OlgaMaciaszek/contracts-demo-
    producer
    https://github.com/OlgaMaciaszek/contracts-demo-
    consumer
    @olga_maciaszek

    View full-size slide

  47. https:/
    /github.com/OlgaMaciaszek
    https:/
    /github.com/OlgaMaciaszek
    @
    @olga_maciaszek
    olga_maciaszek
    Thank you
    Thank you

    View full-size slide