Last year I came across the Karate framework for testing RESTful APIs, it provides a cucumber style Domain Specific Language (DSL) geared towards REST operations and I thoroughly appreciated it.

Typically we use cucumber tests for writing functional stories in cucumber’s Given, When, Then (GWT) synatx and then write step code to make the underlying functionality happen.

This focus on functional aspects doesn’t necessarily translate well to testing cross cutting concerns such as Cross Origin Resource Sharing (CORS) as firstly it is clunky to massage a user story into something so technical in nature. Secondly writing the underlying web client code to match the verbal GWT steps hides much of the specifics of our test preconditions, execution and assertions.

The Mozilla Developer Web Docs have an excellent explanation of CORS here.

Using Karate enables us to clearly see the preconditions, execution and assertions in its DSL and in the CORS example we are able to create three tests:

  1. A CORS preflight request
    Scenario: Attribute Types CORS preflight
      Given url myService + '/attribute-types'
      And header Origin = allowedDomain
      And header Content-Type = 'application/json'
      And header Access-Control-Request-Method = 'GET'
      And header Access-Control-Request-Headers = 'Content-Type'
      When method OPTIONS
      Then status 200
      And match header Access-Control-Allow-Origin == allowedDomain
      And match header Vary contains 'Origin'
    

    In this request we are able to specify the Content-Type, Method, expected Headers and most importantly Origin that our actual request will use in the form of a preflight request.
    A successful preflight request will let a client know that the values they have submitted will be accepted by the server.

  2. A valid API request satisfying the CORS requirements returned from our first test scenario
    Scenario: Attribute Types CORS request
      Given url myService + '/attribute-types'
      And header Origin = allowedDomain
      When method GET
      Then status 200
      And match header Access-Control-Allow-Origin == allowedDomain
      And match header Vary contains 'Origin'
      And assert response.length == 4
    

    All being well thanks to our first test we can expect a 200 OK status from the server where it has accepted our request from a different but allowed Origin.

  3. An invalid API request violating the CORS requirements
    Scenario: Attribute Types CORS invalid request
      Given url myService + '/attribute-types'
      And header Origin = restrictedDomain
      When method GET
      Then status 403
      And match responseHeaders['Vary'][0] contains 'Origin'
      And match responseHeaders['Vary'][1] contains 'Access-Control-Request-Method'
      And match responseHeaders['Vary'][2] contains 'Access-Control-Request-Headers'
    

    Now despite what is technically a valid request we are receiving a 403 Forbidden because our Origin is not in the server’s list of allowed origins, if we were to remove the Origin header completely we will be sending a request as if it were on the same origin, in other words on localhost or from the same domain.

Summary

The real power here is that Karate lets us specify any origin we like, this in combination with variable substitution allows for this test to be structured in such as way we can run it against multiple URLs from multiple origins with various verbs.

We can structure the tests for preflight behaviour, successful requests and forbidden requests in any way we see fit.

I will follow up later on how I have chosen to make these tests more repeatable whilst maintaining rich reporting in my cucumber reports.