Skip to content
This repository has been archived by the owner on Oct 30, 2022. It is now read-only.

Release v0.3.13 #381

Merged
merged 9 commits into from
Feb 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 65 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# [Unmock](https://www.unmock.io/) (JavaScript SDK)

[![CircleCI](https://circleci.com/gh/unmock/unmock-js.svg?style=svg)](https://circleci.com/gh/unmock/unmock-js)
[![CircleCI](https://circleci.com/gh/Meeshkan/unmock-js.svg?style=svg)](https://circleci.com/gh/Meeshkan/unmock-js)
[![codecov](https://codecov.io/gh/unmock/unmock-js/branch/dev/graph/badge.svg)](https://codecov.io/gh/unmock/unmock-js)
[![Known Vulnerabilities](https://snyk.io/test/github/unmock/unmock-js/badge.svg?targetFile=package.json)](https://snyk.io/test/github/unmock/unmock-js?targetFile=package.json)
[![Chat on Gitter](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/unmock/community)
Expand All @@ -23,6 +23,7 @@ Fuzz test your REST API calls.
+ [Ignorable API calls](#ignorable-api-calls)
* [Expectations](#expectations)
* [Initializing Mocks](#initializing-mocks)
* [Faker API](#faker-api)
* [Runner](#runner)
* [OpenAPI](#openapi)
* [Tutorials](#tutorials)
Expand Down Expand Up @@ -370,6 +371,67 @@ github.state((req, schema) =>

The unmock [documentation](https://www.unmock.io/docs/setting-state) contains more information about initializing the state.

## Faker API

`UnmockFaker` class provides a lower-level API for working with mocks. You can create a new `UnmockFaker` via `unmock.faker()`:

```ts
const unmock, { Service, ISerializedRequest} = require("unmock");
// import unmock from "unmock"; // ES6

const faker = unmock.faker();
```

To use the faker for mocking, you need to add services. The first option is to use the `nock` method:

```ts
faker
.nock("http://petstore.swagger.io", "petstore")
.get("/v1/pets")
.reply(200, { foo: u.string() });
```

Alternatively, you can create a service from OpenAPI specification with `Service.fromOpenAPI`:

```ts
const { Service } = require("unmock");

const schema: OpenAPIObject = ...; // Load OpenAPIObject
const petstoreService = Service.fromOpenAPI({ schema, name: "petstore" })

// Add service to `faker`
faker.add(petstoreService);
```

You can then also modify the state of the petstore service via `state` property:

```ts
const { transform } = require("unmock");
// Service should always return code 200
petstore.state(transform.withCodes(200));
```

Once you have added a service, you can use `faker.generate` method to create a mock for any `Request` object:

```ts
const { UnmockRequest, UnmockResponse } = require("unmock");

const req: UnmockRequest = {
host: "petstore.swagger.io",
protocol: "http",
method: "get",
path: "/v1/pets",
pathname: "/v1/pets",
headers: {},
query: {},
}

const res: UnmockResponse = faker.generate(req);

// Access res.statusCode, res.headers, res.body, etc.
expect(res.statusCode).toBe(200);
```

## Runner

The unmock runner runs the same test multiple times with different potential outcomes from the API. All of your unmock tests should use the `runner` unless you are absolutely certain that the API response will be the same every time.
Expand All @@ -388,14 +450,14 @@ Unmock supports the reading of OpenAPI specifications out of the box. Place your

## Tutorials

- [Unmock ts koans](https://github.com/unmock/unmock-ts-koans)
- [Unmock ts koans](https://github.com/meeshkan/unmock-ts-koans)
- [Unmock js katacoda](https://www.katacoda.com/unmock/scenarios/introduction)

## Contributing

Thanks for wanting to contribute! Take a look at our [Contributing Guide](CONTRIBUTING.md) for notes on our commit message conventions and how to run tests.

Please note that this project is governed by the [Unmock Community Code of Conduct](https://github.com/unmock/code-of-conduct). By participating in this project, you agree to abide by its terms.
Please note that this project is governed by the [Meeshkan Community Code of Conduct](https://github.com/meeshkan/code-of-conduct). By participating in this project, you agree to abide by its terms.

## Development

Expand Down
9 changes: 9 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# CHANGELOG.md

## 0.3.13 (2020-02-11)

Features:

- Add programmatic `Faker API` for creating and managing services
- Add `stop` command to `unmock-server`
- Extract `runner` from `unmock-core`
- Add new `unmock-runner` package with `jestRunner` for Jest configuration

## 0.3.12 (2020-01-09)

Fixes:
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"packages": [
"packages/**"
],
"version": "0.3.12"
"version": "0.3.13"
}
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,16 @@
"url": "https://github.com/unmock/unmock-js/issues"
},
"dependencies": {
"openapi-refinements": "file:packages/openapi-refinements",
"unmock": "file:packages/unmock",
"unmock-browser": "file:packages/unmock-browser",
"unmock-cli": "file:packages/unmock-cli",
"unmock-core": "file:packages/unmock-core",
"unmock-fetch": "file:packages/unmock-fetch",
"unmock-jest": "file:packages/unmock-jest",
"unmock-node": "file:packages/unmock-node",
"unmock-runner": "file:packages/unmock-runner",
"unmock-server": "file:packages/unmock-server",
"unmock-browser": "file:packages/unmock-browser",
"openapi-refinements": "file:packages/openapi-refinements",
"unmock-types": "file:packages/unmock-types"
},
"name": "unmock",
Expand Down
2 changes: 1 addition & 1 deletion packages/unmock-browser/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "unmock-browser",
"version": "0.3.12",
"version": "0.3.13",
"description": "Unmock for browser and React Native",
"keywords": [
"unmock",
Expand Down
1 change: 0 additions & 1 deletion packages/unmock-browser/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ if (typeof Buffer === "undefined") {
export const unmock = new UnmockPackage(new RnBackend());

export const nock = unmock.nock.bind(unmock);
export const runner = unmock.runner.bind(unmock);

export default unmock;
2 changes: 1 addition & 1 deletion packages/unmock-cli/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.3.12",
"version": "0.3.13",
"name": "unmock-cli",
"displayName": "unmock-cli",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/unmock-core/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.3.12",
"version": "0.3.13",
"name": "unmock-core",
"displayName": "unmock-core",
"main": "dist/index.js",
Expand Down
49 changes: 47 additions & 2 deletions packages/unmock-core/src/__tests__/faker.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,60 @@
import { u } from "..";
import { Service, transform, u } from "..";
import UnmockFaker from "../faker";
import { ServiceStore } from "../service/serviceStore";
import { PetStoreSchema } from "./utils";

const serviceStore = new ServiceStore([]);
const faker = new UnmockFaker({ serviceStore });

const countServices = (uFaker: UnmockFaker) =>
Object.keys(uFaker.services).length;

const expectNServices = (expectedLength: number) =>
expect(Object.keys(faker.services).length).toEqual(expectedLength);

describe("UnmockFaker", () => {
beforeEach(() => serviceStore.removeAll());
beforeEach(() => faker.purge());

describe("purge()", () => {
it("should remove a service", () => {
faker
.nock("https://foo.com")
.get("/foo")
.reply(200, { foo: u.string() });
expectNServices(1);

faker.purge();
expectNServices(0);
});
});

describe("Adding services with add()", () => {
const petstoreService = Service.fromOpenAPI({
schema: PetStoreSchema,
name: "petstore",
});
beforeEach(() => {
faker.purge();
faker.add(petstoreService);
petstoreService.state(transform.withCodes(200));
});
it("adds a service", () => {
expect(countServices(faker)).toBe(1);
});
it("mocks after a service is added", () => {
const res = faker.generate({
host: "petstore.swagger.io",
protocol: "http",
method: "get",
path: "/v1/pets",
pathname: "/v1/pets",
headers: {},
query: {},
});
expect(res).toHaveProperty("body");
expect(res.statusCode).toBe(200);
});
});

describe("adding service with nock syntax", () => {
it("adds a service", () => {
Expand Down
7 changes: 4 additions & 3 deletions packages/unmock-core/src/backend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ export class Backend {
public readonly serviceStore: ServiceStore = new ServiceStore([]);
public readonly interceptorFactory: IInterceptorFactory;
public readonly serviceDefLoader: IServiceDefLoader;
public readonly randomNumberGenerator: IRandomNumberGenerator;
public faker: UnmockFaker;
public handleRequest?: OnSerializedRequest;
protected readonly requestResponseListeners: IListener[];
Expand All @@ -96,9 +95,11 @@ export class Backend {
this.interceptorFactory = interceptorFactory;
this.requestResponseListeners = listeners || [];
this.serviceDefLoader = serviceDefLoader || NoopServiceDefLoader;
this.randomNumberGenerator = rng || randomNumberGenerator({});
this.loadServices();
this.faker = new UnmockFaker({ serviceStore: this.serviceStore });
this.faker = new UnmockFaker({
serviceStore: this.serviceStore,
randomNumberGenerator: rng || randomNumberGenerator({}),
});
}

public get services(): ServiceStoreType {
Expand Down
50 changes: 46 additions & 4 deletions packages/unmock-core/src/faker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,21 @@ import {
IRandomNumberGenerator,
randomNumberGenerator,
} from "../random-number-generator";
import { Service } from "../service";
import { addFromNock, NockAPI, ServiceStore } from "../service/serviceStore";

export interface IFakerOptions {
export interface IFakerConstructorArguments {
listeners?: IListener[];
serviceStore: ServiceStore;
randomNumberGenerator?: IRandomNumberGenerator;
optionalsProbability?: 1.0;
minItems?: 0;
}

export interface IFakerOptions {
randomNumberGenerator: IRandomNumberGenerator;
minItems: number;
optionalsProbability: number;
}

const DEFAULT_OPTIONS: IUnmockOptions = {
Expand All @@ -34,8 +43,10 @@ export default class UnmockFaker implements IFaker {
* Add a new service to the faker using `nock` syntax.
*/
public readonly nock: NockAPI;
public minItems: number;
public optionalsProbability: number;
public readonly randomNumberGenerator: IRandomNumberGenerator;
private readonly serviceStore: ServiceStore;
private readonly randomNumberGenerator: IRandomNumberGenerator;
private readonly listeners: IListener[];
/**
* Unmock faker. Creates fake responses to fake requests, using
Expand All @@ -47,9 +58,13 @@ export default class UnmockFaker implements IFaker {
listeners,
randomNumberGenerator: rng,
serviceStore,
}: IFakerOptions) {
optionalsProbability,
minItems,
}: IFakerConstructorArguments) {
this.listeners = listeners ? listeners : [];
this.randomNumberGenerator = rng || randomNumberGenerator({});
this.optionalsProbability = optionalsProbability || 1.0;
this.minItems = minItems || 0;
this.serviceStore = serviceStore;
this.createResponse = this.createResponseCreator();
this.nock = addFromNock(this.serviceStore);
Expand Down Expand Up @@ -80,18 +95,45 @@ export default class UnmockFaker implements IFaker {
return (this.serviceStore && this.serviceStore.services) || {};
}

/**
* Add a new service to Faker.
* @param service Service instance.
*/
public add(service: Service) {
this.serviceStore.add(service);
}

/**
* Updates service to Faker. Overwrites existing service with the same name.
* @param service Service instance.
*/
public update(service: Service) {
this.serviceStore.updateOrAddService(service);
}

/**
* Reset the states of all services.
*/
public reset() {
this.serviceStore.resetServices();
}

/**
* Remove all services.
*/
public purge() {
this.serviceStore.removeAll();
}

private createResponseCreator(options?: IUnmockOptions): CreateResponse {
return responseCreatorFactory({
listeners: this.listeners,
options: options || DEFAULT_OPTIONS,
rng: this.randomNumberGenerator,
fakerOptions: {
randomNumberGenerator: this.randomNumberGenerator,
minItems: this.minItems,
optionalsProbability: this.optionalsProbability,
},
store: this.serviceStore,
});
}
Expand Down
Loading