Steve Fitzsimons
Steve Fitzsimons

Reputation: 3904

How to use PactJS with Angular 2/4/5 to tests services

I'm trying to use PactJS to test some of my Angular Services. I'm using:

"@pact-foundation/karma-pact": "^2.1.1",
"pact": "^4.2.1",
"pact-web": "^4.2.1",

I can't get the test to run successfully. Without using async the subscribe callback never gets hit and if I do use async then Pact fails. This is my code:

import { TestBed, async, inject } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { ApiService } from '../index';
import { HttpParams } from '@angular/common/http';
import { Group } from '../../grouping/model';
let Pact = require('pact-web');

describe('ApiService', () => {

  let provider;

  beforeAll((done) => {
    provider = Pact({
      consumer: 'client',
      provider: 'server',
      web: true
    });

    // required for slower CI environments
    setTimeout(done, 2000);

    // Required if run with `singleRun: false`
    provider.removeInteractions();
  });

  afterAll((done) => {
    provider.finalize().then(done, e => done.fail(e));
  });

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule
      ],
      providers: [
        ApiService
      ]
    });
  }));

  afterEach((done) => {
    provider.verify().then(done, e => done.fail(e));
  });

  describe('Get all Groups', () => {

    beforeAll((done) => {
      provider.addInteraction({
        given: 'groups exist',
        uponReceiving: 'a request to get groups',
        withRequest: {
          method: 'GET',
          path: '/api/groups'
        },
        willRespondWith: {
          status: 200,
          headers: {'Content-Type': 'application/json'},
          body: [{
            id: Pact.Matchers.somethingLike(1),
            name: Pact.Matchers.somethingLike('a group name'),
            disabled: Pact.Matchers.somethingLike(false),
          }]
        }
      }).then(done, e => done.fail(e));
    });

    it('should return all groups from API', (done) => {

      const apiService: ApiService = TestBed.get(ApiService);
      const groups: Group[] = [{
        id: 1,
        name: false,
        disabled: false
      }];

      apiService.getGroups().subscribe((response: Group[]) => {
        expect(response).toEqual(groups);
        done()
      });
    });
  });
});

And the errors I get:

Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

Failed: Actual interactions do not match expected interactions for mock MockService.

Missing requests:
    GET /api/groups

See standard out/err for details.

I have been following these two projects

https://paucls.wordpress.com/2017/08/04/pact-consumer-driven-contract-implementation-in-angular/

https://github.com/koelec/angular-pact-example

Has anyone managed to successfully implement PactJS with Angular or what am I missing to get this working?

Upvotes: 3

Views: 862

Answers (2)

J_A_X
J_A_X

Reputation: 12847

The error that's being specified here is specifically because one of your tests timed out (default is 10 seconds), the verify then failed afterwards because of that timed out test. My suggestion is to look at your own code, the ApiService specifically, because it seems like your subscribe callback never gets called:

apiService.getGroups().subscribe((response: Group[]) => {
        expect(response).toEqual(groups);
        done()
      });

Maybe something fails in getGroups() and isn't caught or that subscribe actually doesn't work. Either way, that function never called the endpoint (as per the verify) and the callback never called, hence the timeout since done() was never called.

Upvotes: 0

Matthew Fellows
Matthew Fellows

Reputation: 4065

Your test structure looks good. One way to validate what is happening is to review the logs for the mock server. That error message is pretty clear - it didn't get a request it was expecting.

If HttpClientTestingModule doesn't actually make a real call, then Pact will fail as it is expecting an interaction - this is how we confirm details of the contract that the Provider needs to comply with. If It's mocked out, we don't get the request and can't confirm your code calls what you expect it to.

So. Make sure your tests do in fact make a real HTTP call, and check that they do in fact hit the Mock Server in the right order with the right details (by checking the logs).

Upvotes: 1

Related Questions