|
| 1 | +# slack-mock |
| 2 | + |
| 3 | + |
| 4 | + |
| 5 | +[](https://travis-ci.org/YOU54F/slack-mock-typed) |
| 6 | +[](https://david-dm.org/you54f/slack-mock-typed) |
| 7 | +[](https://david-dm.org/you54f/slack-mock-typed#info=devDependencies) |
| 8 | +[](https://coveralls.io/github/YOU54F/slack-mock-typed?branch=master) |
| 9 | + |
| 10 | + |
| 11 | + |
| 12 | +A Slack API mocker for all your Slack bot and Slack app integration tests. |
| 13 | + |
| 14 | +## Mock All Slack APIs |
| 15 | + |
| 16 | +Slack Mock will mock a way of pushing data into Slack. You can use it to mock calls to |
| 17 | +- [Incoming Webhooks](https://api.slack.com/incoming-webhooks) |
| 18 | + |
| 19 | +You can use your API calls as is without changing any URLs or tokens. Slack Mock will capture all outbound HTTP requests to `https://slack.com` and `https://hooks.slack.com`, so Slack will never receive your API calls. |
| 20 | + |
| 21 | +With Slack-Mock you can inspect all outbound requests and trigger incoming requests to make sure your bot is doing the right thing. |
| 22 | + |
| 23 | +## No Magic Included |
| 24 | + |
| 25 | +OK, there's a little magic included in capturing HTTP requests, but that's it. No timeouts, magic promises, or events. Integration tests are hard, trying to make them easy with "convenience" abstractions that are out of your control only makes them harder. |
| 26 | + |
| 27 | +Integration test by their nature are testing a closed system: you are inspecting from the outside a complex flow between at least two entities (your bot and the Slack API) and there is no guaranteed way to know when that flow is complete by observing from the outside. Any attempt to guess when the communication is complete will be wrong some of the time and just cause you frustration. |
| 28 | + |
| 29 | +That's why Slack Mock provides simple, synchronous methods to queue, trigger, and inspect messages to and from Slack. No magic included. |
| 30 | + |
| 31 | +To write a Slack Mock integration test queue up responses from Slack to your bot, then use Slack Mock to send a message from Slack to your bot to trigger a bot action, wait some time, then assert that your bot made the correct calls to Slack in order. How long do you wait? It depends on what your bot is doing. Play around a little and see what works. I find a 50 millisecond wait is more than enough for most flows. |
| 32 | + |
| 33 | +## Usage |
| 34 | + |
| 35 | +### Incoming Webhooks |
| 36 | + |
| 37 | +```ts |
| 38 | +// incoming webhooks |
| 39 | + |
| 40 | +import * as SlackMock from "slack-mock-typed"; |
| 41 | +const mock: SlackMock.Instance = SlackMock.SlackMocker({ logLevel: "debug" }); |
| 42 | + |
| 43 | +function setup() { |
| 44 | + beforeAll(async () => { |
| 45 | + jest.setTimeout(60000); |
| 46 | + await mock.incomingWebhooks.start(); |
| 47 | + await mock.incomingWebhooks.reset(); |
| 48 | + }); |
| 49 | + |
| 50 | + beforeEach(async () => { |
| 51 | + jest.resetModules(); |
| 52 | + await mock.incomingWebhooks.reset(); |
| 53 | + expect(mock.incomingWebhooks.calls).toHaveLength(0); |
| 54 | + }); |
| 55 | + afterEach(async () => { |
| 56 | + await mock.incomingWebhooks.reset(); |
| 57 | + }); |
| 58 | +} |
| 59 | + |
| 60 | +function returnMockedSlackWebhookCall() { |
| 61 | + expect(mock.incomingWebhooks.calls).toHaveLength(1); |
| 62 | + const firstCall = mock.incomingWebhooks.calls[0]; |
| 63 | + expect(firstCall.url).toEqual(process.env.SLACK_WEBHOOK_URL); |
| 64 | + const body = firstCall.params; |
| 65 | + return body; |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +## API Conventions |
| 70 | + |
| 71 | +Slack Mock will intercept all requests to `https://hooks.slack.com`. There's no need to change any URLs in your bot. |
| 72 | + |
| 73 | +Here are the method conventions. Not every API wrapper supports each of these methods, see the [API docs](#api) below: |
| 74 | +- `addResponse` will add the next response returned. You can call mutlitple times to queue responses. If you set a `url` option, then the response will only be returned for that url. URL specific responses take precedence over unspecified responses |
| 75 | +- `calls` will be in order received and always contain params, headers, and url. Params contain both query params and body properties. |
| 76 | +- `reset` will always clear calls and any queued responses you have. |
| 77 | + |
| 78 | +There is also a top level `reset` convenience method that will call reset on each API wrapper. |
| 79 | + |
| 80 | +Slack mock will respond to all requests with a 200 OK unless a custom response has been queued. For web requests, a the default body will be `{ok: true}`. |
| 81 | + |
| 82 | +## API |
| 83 | + |
| 84 | +### `require('slack-mock')`: `function(config)` |
| 85 | + |
| 86 | +The exported function used to start the Slack Mock server. Returns an instance of the server. |
| 87 | + |
| 88 | +Slack Mock is a singleton so can only be configured once per process. Subsequent calls to SlackMocker() will return |
| 89 | +the same instance. |
| 90 | + |
| 91 | +Config options are: |
| 92 | + - `logLevel` (String, optional) The log level to use. One of `error`, `warn`, `info`, `verbose`, `debug`, or `silly`. Defaults to `info`. |
| 93 | + |
| 94 | +--- |
| 95 | + |
| 96 | +### `instance` |
| 97 | + |
| 98 | +The configured instance of the Slack Mock `SlackMocker.instance` object. This is the same object returned from `require('slack-mock')(config)`. |
| 99 | + |
| 100 | +--- |
| 101 | + |
| 102 | +### `instance.incomingWebhooks` (Incoming Webhooks) |
| 103 | + |
| 104 | +The `incomingWebhooks` object mocks receiving payloads from you Slack App to all Incoming Webhooks at `https://hooks.slack.com/`. |
| 105 | + |
| 106 | +- `addResponse`: `function(opts)` Queues a response payload that Slack Mock will use to respond upon |
| 107 | +receiving a post to a registered endpoint. This method can be called multiple times. Responses |
| 108 | +will be used in a FIFO order. Options are: |
| 109 | + - `url` (String, optional) The Incoming Webhook URL your app will be POSTing to. |
| 110 | + - `statusCode` (Number, optional) The HTTP status code to reply with. Defaults to 200. |
| 111 | + - `body` (Object, optional) The response body to reply with. Defaults to `OK`. |
| 112 | + - `headers` (Object, optional) The HTTP headers to reply with. Defaults to `{}`. |
| 113 | + |
| 114 | +- `reset`: `function()` Empties the `incomingWebhooks.calls` array and clears any queued responses. |
| 115 | + |
| 116 | +- `calls`: `Array` An array of payloads received your from Slack app to an Incoming Webhook url. |
| 117 | + - `url` The url of the call that was intercepted. |
| 118 | + - `params` The POST body merged with any query string parameters captured from the intercepted request as an Object. |
| 119 | + - `headers` The headers of the intercepted request as an Object. |
| 120 | + |
| 121 | + |
| 122 | +### `instance.reset`: `function()` |
| 123 | + |
| 124 | +Resets all mocks. A convenience method for calling reset on individual API mocks. |
0 commit comments