Skip to content

Commit

Permalink
docs: add alternate examples to highlight jest-pact's contribution
Browse files Browse the repository at this point in the history
  • Loading branch information
TimothyJones committed Jun 16, 2019
1 parent 70db2b9 commit 066706d
Showing 1 changed file with 130 additions and 127 deletions.
257 changes: 130 additions & 127 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
[![devDependency Status](https://img.shields.io/david/dev/you54f/jest-pact.svg?style=flat-square)](https://david-dm.org/you54f/jest-pact#info=devDependencies)
[![CircleCI](https://circleci.com/gh/YOU54F/jest-pact.svg?style=svg)](https://circleci.com/gh/YOU54F/jest-pact)


## Jest Adaptor to help write Pact files with ease

### Features
Expand Down Expand Up @@ -37,165 +36,169 @@ yarn add jest-pact --dev

## Usage

```ts
import { pactWith } from 'jest-pact';

```js
pactWith({ consumer: 'MyConsumer', provider: 'MyProvider' }, provider => {
// regular pact tests go here
}
```
## Configuration
```ts
## Example
pactWith({PactOptions}, provider => {
// regular pact tests go here
}
Say that your API layer looks something like this:
export interface PactOptions {
provider: string;
consumer: string;
port?: number; // defaults to a random port if not provided
pactfileWriteMode?: PactFileWriteMode;
}
```js
import axios from 'axios';

export declare type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
export declare type PactFileWriteMode = "overwrite" | "update" | "merge";
const defaultBaseUrl = "http://your-api.example.com"

export const api = (baseUrl = defaultBaseUrl) => ({
getHealth: () => axios.get(`${baseUrl}/health`)
.then(response => response.data.status)
/* other endpoints here */
})
```
## Output
- Log files are written to /pact/logs
- Pact files are written to /pact/pacts
## Example
Then your test might look like:
A contrived example using supertest as a client
```ts
import {InteractionObject} from "@pact-foundation/pact"
import * as jestpact from "jest-pact";
import * as supertest from "supertest";

jestpact.pactWith(
{ consumer: "test-consumer", provider: "json-provider" },
async (provider: any) => {
const client = () => {
const url = `${provider.mockService.baseUrl}`;
return supertest(url);
};
test("should accept a valid get request to get a pet", async () => {
const postValidRequest: InteractionObject = {
state: "A pet 1845563262948980200 exists",
uponReceiving: "A get request to get a pet",
```js
import { pactWith } from 'jest-pact';
import { Matchers } from '@pact-foundation/pact';
import api from 'yourCode';

pactWith({ consumer: 'toolkit-ui', provider: 'toolkit-api' }, provider => {
let client;

beforeEach(() => {
client = api(provider.mockService.baseUrl)
});

describe('health endpoint', () => {
// Here we set up the interaction that the Pact
// mock provider will expect.
//
// jest-pact takes care of validating and tearing
// down the provider for you.
beforeEach(() =>
provider.addInteraction({
state: "Server is healthy",
uponReceiving: 'A request for API health',
willRespondWith: {
status: 200
status: 200,
body: {
status: Matchers.like('up'),
},
},
withRequest: {
method: "GET",
path: "/v2/pet/1845563262948980200",
headers: { api_key: "[]" }
}
};
await provider.addInteraction(postValidRequest);

await client()
.get("/v2/pet/1845563262948980200")
.set("api_key", "[]")
.expect(200);
await provider.verify();
});
}
);
method: 'GET',
path: '/health',
},
})
);

// You also test that the API returns the correct
// response to the data layer.
//
// Although Pact will ensure that the provider
// returned the expected object, you need to test that
// your code recieves the right object.
//
// This is often the same as the object that was
// in the network response, but (as illustrated
// here) not always.
it('returns server health', () =>
client.health().then(health => {
expect(health).toEqual('up');
}));
});

```
To use Pact to it's full effect, you should replace the client above with your API call in your code and instantiate with pact mock service base url `provider.mockService.baseUrl`
You can make your tests easier to read by extracting your request and responses:
So if your calling method is
```ts
export const api = (baseURl) => ({
getUser: () => axios(opts).then(processResponse)
})
```js
/* pact.fixtures.js */
import { Matchers } from '@pact-foundation/pact';

export const healthRequest = {
uponReceiving: 'A request for API health',
withRequest: {
method: 'GET',
path: '/health',
},
};

export const healthyResponse = {
status: 200,
body: {
status: Matchers.like('up'),
},
}
```
Then your test may look like
```ts
import {InteractionObject} from "@pact-foundation/pact"
import * as jestpact from "jest-pact";
import {api} from "yourCode";

jestpact.pactWith(
{ consumer: "test-consumer", provider: "json-provider" },
async (provider: any) => {
const client = () => {
const url = `${provider.mockService.baseUrl}`;
return api(url);
};
test("should accept a valid get request to get a pet", async () => {
const postValidRequest: InteractionObject = {
state: "A pet 1845563262948980200 exists",
uponReceiving: "A get request to get a pet",
willRespondWith: {
status: 200
},
withRequest: {
method: "GET",
path: "/v2/pet/1845563262948980200",
headers: { api_key: "[]" }
}
};
await provider.addInteraction(postValidRequest);

await client()
.get("/v2/pet/1845563262948980200")
.set("api_key", "[]")
.expect(200);
await provider.verify();
});
}
);
```js
import { pactWith } from 'jest-pact';
import { healthRequest, healthyResponse } from "./pact.fixtures";

import api from 'yourCode';

pactWith({ consumer: 'toolkit-ui', provider: 'toolkit-api' }, provider => {
let client;

beforeEach(() => {
client = api(provider.mockService.baseUrl)
});

describe('health endpoint', () => {

beforeEach(() =>
provider.addInteraction({
state: "Server is healthy",
...healthRequest,
willRespondWith: healthyResponse
})
);

it('returns server health', () =>
client.health().then(health => {
expect(health).toEqual('up');
}));
});
```
You can make your test shorter, by moving your interaction object into another file
## Configuration
```ts
import * as jestpact from "jest-pact";
import * as supertest from "supertest";
import * as interaction from "./expectation/json.expectation";
import * as json from "./requestResponse/json.reqRes";

jestpact.pactWith(
{ consumer: "test-consumer", provider: "json-provider" },
async (provider: any) => {
const client = () => {
const url = `${provider.mockService.baseUrl}`;
return supertest(url);
};
test("should accept a valid get request to get a pet", async () => {
await provider.addInteraction(interaction.postValidRequest);

await client()
.get("/v2/pet/1845563262948980200")
.set("api_key", "[]")
.expect(200, json.getPetValidResponse);
await provider.verify();
});
}
);

pactWith(PactOptions, provider => {
// regular pact tests go here
}

interface PactOptions {
provider: string;
consumer: string;
port?: number; // defaults to a random port if not provided
pactfileWriteMode?: PactFileWriteMode;
}

type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
type PactFileWriteMode = "overwrite" | "update" | "merge";

```
## Defaults
- Log files are written to /pact/logs
- Pact files are written to /pact/pacts
### Jest Watch Mode
By default Jest will watch all your files for changes, which means it will run in an infinite loop as your pact tests will generate json pact files and log files.
You can get round this by using the following `watchPathIgnorePatterns: ["pact/logs/*","pact/pacts/*"]` in your `jest.config.js`
You can get round this by using the following `watchPathIgnorePatterns: ["pact/logs/*","pact/pacts/*"]` in your `jest.config.js`
Example
Expand All @@ -213,7 +216,7 @@ module.exports = {
testMatch: ["**/*.test.(ts|js)", "**/*.it.(ts|js)", "**/*.pacttest.(ts|js)"],
testEnvironment: "node",
reporters: ["default", "jest-junit"],
watchPathIgnorePatterns: ["pact/logs/*","pact/pacts/*"]
watchPathIgnorePatterns: ["pact/logs/*", "pact/pacts/*"]
};
```
Expand Down

0 comments on commit 066706d

Please sign in to comment.