0.0.6 • Published 7 years ago

angular-http-raml v0.0.6

Weekly downloads
10
License
MIT
Repository
github
Last release
7 years ago

Table of Contents generated with DocToc

angular-http-raml

This library provides a RAML RAML-based MockBackend generator for testing Angular 4 applications.

Simply put, you can define your REST API using RAML, and use this definition to create an angular Http object which speaks the same language as the API you defined.

Installation

Install the library with npm install --save-dev angular-http-raml

Then add the following entry to your karma configuration:

files: [
  ...
  {pattern: "**/*.raml", included: false}
  ...
],

Stubbing

Prerequisities:

To understand the below walk-through, you need to have a basic understanding of the followings:

  • Angular 2+
  • RAML
  • Jasmine
  • Test doubles (mocks and stubs)

Quickstart

This library generates stub HTTP backends

Let's assume your application interacts with the API defined by the following RAML file:

person-api.raml:

#%RAML 1.0
title: Endpoints for testing response selection based on status codes
version: v0
baseUri: http://api.example.com
mediaType: application/json
/person/{personId}/emails:
  get:
    responses:
      200:
        queryParameters:
          activeOnly: boolean 
        body:
          example:
            - email: "testuser@example.com"
              active: true
            - email: "test.user@example.com"
              active: false
      404:
        body:
          example:
            message: "not found"
  post:
    responses:
      201:

Let's also assume that you want to test the following Angular service:

my-service.ts:

export class MyService {
  
  constructor(private http: Http) {}
  
  public fetchPersonEmails(personId: number): Observable<string[]> {
    return http.get("http://api.example.com/person/" + personId + "/emails")
        .map(response => response.json());
  }
  
}

my-service.spec.ts :

describe("MyService", () => {
  
  it("queries the backend", () => {
    const mockBackend = RAMLBackendConfig.initWithFile("./person-api.raml")
      .stubAll()
      .createBackend();
    const http = new Http(mockBackend, new RequestOptions());
    const subject = new MyService(http);
    
    subject.fetchPersonEmails(123).subscribe(emails => {
      expect(emails).toEqual([
        {email: "testuser@example.com", active: true},
        {email: "test.user@example.com", active: false},
      ]);
    });
    
    mockBackend.verifyNoPendingRequests();
  });
  
});

Here is what happens when you run this test:

  • the RAMLBackendConfig.initWithFile("./person-api.raml") call looks up the REST endpoints with their parameters. These are the possible calls to be used on the mock
  • the stubAll() call tells the RAMLBackendConfig instance to stub all requests defined in the RAML file. The responses will be the entities defined in the example node of the RAML definition (if present).
  • then we instantiate a Http object which will use our generated MockBackend.
  • when your MyService method calls the http.get(...) method then the generated stub will know that the GET http://api.example.com/person/123/emails call matches the stubbed GET /person/{personId}/emails endpoint, so it will pick up the example array and return it as the response body. Unless you specify it otherwise it will look for a 2xx response in the listed responses.
  • the MyService instance receives the response (just like if it would be a real HTTP backend service), and publishes the result to the subscriber attached in the test. This subscriber will perform the assertion (if the response of MyService is correct).

This was the quick-start of using the library. To sum up, if you develop a RAML file which includes example responses, then you generate a HTTP stub backend from it in a few lines.

Customizing stubbing

Changing the response by response code

RAML lets you describe responses with multiple response codes. By default, angular-http-raml will choose the response with the lowest 2xx status code, but you can simply override this behavior. Example (using the above listed person-api.raml):

// ...
  const mockBackend = RAMLBackendConfig.initWithFile("./person-api.raml")
   .stubAll()
   .whenGET("/person/123/emails").thenRespondWith(404)
   .createBackend();
// ...

This configuration will override the default stubbing and tell the RAMLBackend to send the 404 response instead of the default 200. The response body will be {"message":"not found"} as it is defined in the RAML file.

Using an other example response body

RAML lets you define multiple example response bodies for a response. angular-http-raml lets you easily switch between them. Let's consider the following RAML definition of an endpoint:

/person/{personId}/emails:
  get:
    responses:
      200:
        body:
          examples:
            hasEmails:
              - email: "testuser@example.com"
                active: true
              - email: "test.user@example.com"
                active: false
            emptyList: []

In this RAML definition there are 2 example responses (both with 200 response code). They are called hasEmails and emptyList. By default the RAMLBackend instance would return the first example response it finds (hasEmails), override it by passing emptyList as the 2nd parameter of thenRespondWith():

const mockBackend = RAMLBackendConfig.initWithFile("./person-api.raml")
  .stubAll()
  .whenGET("/person/123/emails").thenRespondWith(404, "emptyList")
  .createBackend();

Passing an entire request

If the above two methods of overriding default responses - eg. because the response body you want to test with is different from the examples available in RAML - then you can explicitly pecify the entire request to be sent back from the server by calling thenRespond() instead of thenRespondWith() :

const mockBackend = RAMLBackendConfig.initWithFile("./person-api.raml")
      .whenGET("/person/123/emails").thenRespond(new Response(new ResponseOptions({
        status: 200,
        body: JSON.stringify([{email:null, active: false}]);
      })))
      .createBackend();

Safety nets while stubbing

One thing that can go wrong while stubbing - or creating any kind of test double - is mimicing false behavior of the stubbed system. As a simple example, if you mistype a request path in your test, then you implement a service that conforms to the test, then your test will pass, but your service still won't be compatible with your backend.

To minimize the risk of such problems, angular-http-raml validates your stubbed requests, so it checks if you properly mimic the behavior of the remote service. In the below example the stub request contains a query parameter called onlyActive, but since this query parameter is not included in the RAML definition, an InvalidStubbingError will be thrown:

const mockBackend = RAMLBackendConfig.initWithFile("./person-api.raml")
 // thow will throw an InvalidStubbingError immediately,
 // since "onlyActive" is not a valid query param
  .whenGET("/person/123/emails?onlyActive=true").thenRespond(200)
  .createBackend();

Similarly, eg. if you try to stub a request which doesn't match any path patterns then an error is thrown.

Does it mean that you can't have any compatibility issues with server-client communication? Of course, not. The library conforms to your RAML documentation and not to your real server. To take a full advantage of RAML, it is recommended to involve the same file in testing your backend, so the contract is verified from both ends. This library can help you with only the half of the job.

Mocking

TODO