Link Search Menu Expand Document

Getting started

Setup

The quickest approach to getting started is through the command line.

Download the standalone Jar.

https://github.com/qontract/qontract/releases/download/0.13.1/qontract.jar

Mac / Linux

alias qontract='java -jar <basedir>/qontract.jar'

Windows

Create a batch file with below content.

java -jar <basedir>/qontract.jar %*

The %* portion tells the batch script to pass all of the parameters it receives to the new command.


Example Application - PetStore

PetStore application has a backend API (Provider) and a front-end client application (Consumer). Below is a sequence diagram.

UI (Consumer)          API (Provider)
      | --- getPetById ---> |
      | <-- {Pet JSON} ---- |

Authoring a contract

Let us try specifying the above interaction as a contract.

Copy paste below text into a file with name “service.qontract”. This, as you can see, uses the Gherkin syntax to describe a basic GET request.

Feature: Contract for the petstore service

Scenario: Should be able to get a pet by petId
  When GET /pets/(petid:number)
  Then status 200
  And response-body {petid: "(number)"}

There are some extra keywords that make it easier to define APIs.

  • GET and related URL
  • status
  • response-body
  • (number) - placeholder for number datatype

These keywords are documented in the contract syntax reference. TODO


Consumer Side - Contract As A Stub

Let us start with the Consumer in this example. We will be running a Stub / Mock Server based on the above contract file.

UI (Consumer)         Qontract Stub <- service.qontract
      | --- getPetById ---> |
      | <-- {Pet JSON} ---- |

To spin up a stub server with the service.qontract we authored earlier, run below command.

qontract stub "<base-dir>/petstore/qontract/service.qontract" --host="localhost" --port="8000"

The command has defaults and necessary help to guide you through.

Once the stub server is running you can verify the API by accessing it through Postman, Chrome, Curl etc. to see the response.

>curl http://localhost:8000/pets/123
{"petid":180}

The response contains auto-generated values that adhere to the data type defined in the contract.

Example: In above output petid “180” is generated by qontract and will vary with every execution. If you would like to control the value that is being generated please refer to suggestions.

We can now start consumer development against this stub without any dependency on the real API.


Provider Side - Contract as a Test

Qontract runs Contract Tests on API to make sure it adheres to the contract.

service.qontract -> Qontract Test        API (Provider)
                        | --- getPetById ---> |
                        | <-- {Pet JSON} ---- |

Below command runs service.qontract as a contract test against the provider.

qontract test "<base-dir>/petstore/contract/service.qontract" --host="localhost" --port="8000"

You should see below error message.

Scenario: Should be able to get a pet by petId GET /pets/(id:number) FAILED
Reason: Connection refused
Tests run: 1, Failures: 1

This make sense because we have not written code for the Provider yet. Also notice each scenario is considered as a test. Which is why is you see Test run: 1 and Failures: 1.

Let us spin up a API to get this test to pass. I am going to leverage Sinatra. However you could run any server.

# server.rb
require 'sinatra'

get '/' do
  'Welcome to Petstore!'
end

After I start the above server, I run the command again.

>> Request Start At Thu Jun 18 11:42:47 IST 2020
-> GET /pets/320
-> Accept-Charset: UTF-8
-> Accept: */*
->
->
<- 404 Not Found
<- Content-Type: text/html;charset=utf-8
<- X-Cascade: pass
<- Content-Length: 471
<- X-Xss-Protection: 1; mode=block
<- X-Content-Type-Options: nosniff
<- X-Frame-Options: SAMEORIGIN
<- Server: WEBrick/1.6.0 (Ruby/2.7.1/2020-03-31)
<- Date: Thu, 18 Jun 2020 06:12:47 GMT
<- Connection: Keep-Alive
<-
<- <!DOCTYPE html>
<- <html>
<- <head>
<-   <style type="text/css">
<-   body { text-align:center;font-family:helvetica,arial;font-size:22px;
<-     color:#888;margin:20px}
<-   #c {margin:0 auto;width:500px;text-align:left}
<-   </style>
<- </head>
<- <body>
<-   <h2>Sinatra doesn’t know this ditty.</h2>
<-   <img src='http://localhost:8000/__sinatra__/404.png'>
<-   <div id="c">
<-     Try this:
<-     <pre>get &#x27;&#x2F;pets&#x2F;320&#x27; do
<-   &quot;Hello World&quot;
<- end
<- </pre>
<-   </div>
<- </body>
<- </html>
<< Response At Thu Jun 18 11:42:47 IST 2020 ==


Scenario: Should be able to get a pet by petId GET /pets/(petid:number) FAILED
Reason: Testing scenario "Should be able to get a pet by petId"
    >> RESPONSE.STATUS

    Expected status: 200, actual: 404


Tests run: 1, Failures: 1

This time it is a different error and it rightly points out that the petstore API does not support /pets/:id endpoint. It also prints request and response to help us debug.

Let us add the /pets/:id endpoint to our API.

# server.rb
require 'sinatra'
require 'json'

get "/pets/:id" do
  content_type :json
  "{ petid: #{params['id']} }"
end

When we run the command this time we see success.

>> Request Start At Thu Jun 18 11:55:20 IST 2020
-> GET /pets/899
-> Accept-Charset: UTF-8
-> Accept: */*
->
->
<- 200 OK
<- Content-Type: application/json
<- Content-Length: 14
<- X-Content-Type-Options: nosniff
<- Server: WEBrick/1.6.0 (Ruby/2.7.1/2020-03-31)
<- Date: Thu, 18 Jun 2020 06:25:20 GMT
<- Connection: Keep-Alive
<-
<- {
<-     "petid": 899
<- }
<< Response At Thu Jun 18 11:55:20 IST 2020 ==


Scenario: Should be able to get a pet by petId GET /pets/(petid:number) SUCCESSFUL

Tests run: 1, Failures: 0

You may have noticed that we started with a contract and then leveraged it as a test to drive the API development. This is quite similar to BDD.


Got another 10 minutes? Try the programmatic approach