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

Creating and testing REST and Messaging contracts with Spring Cloud Contract - JDD 2018

Creating and testing REST and Messaging contracts with Spring Cloud Contract - JDD 2018

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

October 09, 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
    09.10.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
    Stubbed
    Environments
    Environments
    @olga_maciaszek

    View full-size slide

  12. Invalid Stubs - REST
    Invalid Stubs - REST
    @olga_maciaszek
    {
    “request” :
    {
    “method”: “GET”,
    “url” : “/endpointX”,
    },
    “response” :
    {
    “status” : 200
    }
    {
    “request” :
    {
    “method”: “GET”,
    “url” : “/endpointY”,
    },
    “response” :
    {
    “status” : 200
    }

    View full-size slide

  13. Invalid Stubs - Messaging
    Invalid Stubs - Messaging
    @olga_maciaszek
    topic: foo
    class Product {
    int productId
    String productName
    }
    topic: bar
    class Product {
    int productId
    String productName
    }
    topic: foo
    class Product {
    int id
    String productName
    }

    View full-size slide

  14. Stubbed Environments
    Stubbed Environments
    @olga_maciaszek
    Outdated Stubs
    Outdated Stubs

    View full-size slide

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

    View full-size slide

  16. Input A ==> Output B
    Input A ==> Output B
    @olga_maciaszek
    Contracts
    Contracts
    [Consumer] [Contract] [Producer]

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  20. SCC Flow
    SCC Flow
    @olga_maciaszek

    View full-size slide

  21. SCC Flow
    SCC Flow
    @olga_maciaszek

    View full-size slide

  22. 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

  23. 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

  24. SCC Flow
    SCC Flow
    @olga_maciaszek

    View full-size slide

  25. 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

  26. 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

  27. 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

  28. SCC Flow
    SCC Flow
    @olga_maciaszek

    View full-size slide

  29. SCC Flow
    SCC Flow
    @olga_maciaszek

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  32. 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
    @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

  33. 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

  34. Code Demo
    Code Demo
    @olga_maciaszek

    View full-size slide

  35. 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

  36. 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

  37. 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

  38. Code Demo
    Code Demo
    @olga_maciaszek

    View full-size slide

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

    View full-size slide

  40. Other Technologies
    Other Technologies
    mvn spring-cloud-contract:run -Dstubs="org.springframework:gs-rest-service"
    Docs
    @olga_maciaszek

    View full-size slide

  41. Other Technologies
    Other Technologies
    @olga_maciaszek
    Docker
    Docker

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  45. 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

  46. Architecture TDD
    Architecture TDD
    @olga_maciaszek

    View full-size slide

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

    View full-size slide

  48. Deferred Updates
    Deferred Updates
    @olga_maciaszek

    View full-size slide

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

    View full-size slide

  50. Demo Code Links
    Demo Code Links
    https://github.com/OlgaMaciaszek/contracts-demo-
    producer
    https://github.com/OlgaMaciaszek/contracts-demo-
    consumer
    https://github.com/OlgaMaciaszek/scc-payment
    https://github.com/OlgaMaciaszek/scc-shop
    @olga_maciaszek

    View full-size slide

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

    View full-size slide