Repository: pmcelhaney/counterfact
Pull request #88 (2022-07-18 19:12:42)

configure JSON Schema Faker to use the examples


Patch:
diff --git a/.changeset/wicked-dingos-lay.md b/.changeset/wicked-dingos-lay.md
new file mode 100644
index 0000000..0917557
--- /dev/null
+++ b/.changeset/wicked-dingos-lay.md
@@ -0,0 +1,5 @@
+---
+"counterfact": minor
+---
+
+use example values (if present) when generating random data
diff --git a/README.md b/README.md
index 2bbca64..2dc3ec5 100644
--- a/README.md
+++ b/README.md
@@ -2,76 +2,13 @@
 
 [![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fpmcelhaney%2Fcounterfact%2Fmain)](https://dashboard.stryker-mutator.io/reports/github.com/pmcelhaney/counterfact/main)
 
-Counterfact is (will be) a tool that helps front end developers and back end developers collaborate by quickly building reference implementations of OpenAPI specs.
+## Why?
 
-Counterfact is _not_ for _production_.
+I was building an UI against back end code that was so cumbersome I spent almost as much time trying to get the latest version of the back end code running as I did writing the front end code. Also I needed to build UI code against features and bug fixes that would not be available for a long time. Eventually I determined in the long run it would be quicker to hack together a fake implementation of the API so that I could keep going. Over time that fake implementation evolved to the point that it was pretty nice to work with. And I loved that I could easily recreate the exact scenarios I needed to test against.
 
-Counterfact is _great_ for _testing the UI_, both manual and automated testing. UI testing can be quite tedious and brittle in part because the _arrange_ part of Arrange, Act, Assert -- getting the server into a particular state -- is often slow, unpredictable, and complex. Counterfact bypasses the accidental complexity associated with arranging a real server's state, making tests that isolate the UI fast, repeatable, and easy to maintain.
+After leaving that job and leaving the code behind, I looked around for open source tools that do more or less the same thing, and couldn't find any that were as malleable and flexible as the code that I had hacked together. So I recreated it as an open source library. Not hacked together this time, but carefully constructed with test driven development.
 
-It accomplishes that without over-isolating. You can run a full scenario: log in, make changes, log in with a different user, and see the effects of those changes. The server you're testing against is a _real_ server from the UI's perspective: it implements business logic and maintains state. It's only not real in the sense that it doesn't have to support 10,000 concurrent users, protect real data, go through a deployment pipeline, or maintain 99.99% uptime.
-
-Counterfact is _great_ for _communication between front end and back end teams_. OpenAPI does an excellent job defining the inputs and outputs of a RESTful service, but it can't tell us how a server is supposed to behave. Counterfact fills that gap. The feedback cycle is orders of magnitude faster than making changes to a real, scalable back end and then seeing how those changes affect the UI/UX.
-
-## Road to 1.0
-
-This project is a work in progress. As of July 15, 2022, most of the plumbing is in place. This project will reach 1.0 when mutation test coverage is > 90% and:
-
-The following command
-
-```sh
-npx counterfact init ./my-petstore https://petstore3.swagger.io/api/v3/openapi.yaml
-```
-
-creates or augments an npm package, and after changing to the directory
-
-```sh
-cd my-petstore
-```
-
-the following command
-
-```
-yarn start
-```
-
-opens a browser with Swagger-UI pointing to a server that implements the pet store API.
-
-Not a mock server that returns canned or randomly generated responses, a [_real_ implementation](./demo-ts/), written in TypeScript.
-
-Or at least, the _potential_ for a real implementation. Out of the box, it will return example or random responses. But the code is easy to edit so, e.g., we can change `PUT /pet` to store a `Pet` in memory / a database / a microservice / etc., and `GET /pet` to retrieve that same `Pet`.
-
-We can see the effects of the code changes _without restarting the server_.
-
-And then after running
-
-```sh
-yarn generate /path/to/copy/of/petstore/with/some/changes.yaml
-```
-
-it regenerates the code except for the files that we changed. Of the files we changed, the IDE (via TypeScript and ESLint) tells us if the code needs to be modified to conform to the updated spec.
-
-_Again, without restarting the server._
-
-## Beyond 1.0
-
-The scenario above describes a zero-configuration happy path, adhering to the philosophy of convention over configuration.
-
-The next phase is to add configuration options that define things like what port the server runs on, when generated files are overwritten, where those files live, whether to add `start` and `generate` scripts and what they should be named, whether to build a soup-to-nuts server or only make Express / Koa / Connect middleware available, etc. These configurations may be set in a `.counterfactrc.json` file, environment variables, and or command line options, whatever makes sense.
-
-(By the way, generating code is optional. When this project started, that wasn't even part of the scope. If you don't have an OpenAPI spec, or don't want to use it, Counterfact is still an ergonomic platform on which to build quick and dirty prototypes of RESTful services in TypeScript or JavaScript.)
-
-After that, more features that improve developer experience. For example, say you're demoing an iOS music player app and someone asks about edge cases. You'll be able answer questions immediately by going into a REPL and typing things like:
-
-```ts
-context.addABunchOfSongsToCatalog(1_000_000);
-
-context.setAlbumTitle(
-  123,
-  "This Is is a Very Long Name That is Definitely Not Going to Fit in the Allotted Space and it has Emoji 🦧"
-);
-
-network.setLatency("1-5s");
-```
+With Counterfact, we can implement a server with quick-and-dirty JavaScript code and _that's totally okay_. We're not supposed to use Counterfact in production any more than we're supposed to build a skyscraper out of Play-Doh. The design is optimized for testing scenarios quickly and painlessly. Changes are picked up immediately, without compiling or restarting the server. It's great for manual testing, especially hard to reproduce bugs. It's also great for automated testing.
 
 ## Installation
 
@@ -87,7 +24,7 @@ yarn add -D counterfact
 
 ## Usage
 
-See the [demo-ts](./demo-ts/README.md) directory for an example.
+See the [demo](./demo/README.md) directory for an example.
 
 ### Using the Koa plugin
 
@@ -106,7 +43,7 @@ const app = new Koa();
 const initialContext = {};
 
 const { koaMiddleware } = await counterfact(
-  fileURLToPath(new URL("paths/", import.meta.url)),
+  fileURLToPath(new URL("routes/", import.meta.url)),
   initialContext
 );
 
@@ -115,15 +52,15 @@ app.use(koaMiddleware);
 app.listen(PORT);
 ```
 
-### Setting up paths
+### Setting up routes
 
-The first thing you need to do is create a directory where you will put your paths implementations.
+The first thing you need to do is create a directory where you will put your routes.
 
 ```sh
-mkdir paths
+mkdir routes
 ```
 
-To mock an API at `/hello/world`, create a file called `./paths/hello/world.js`.
+To mock an API at `/hello/world`, create a file called `./routes/hello/world.js`.
 
 Inside that file, output a function that handles a GET request.
 
diff --git a/src/tools.js b/src/tools.js
index c1ce71d..e71dc67 100644
--- a/src/tools.js
+++ b/src/tools.js
@@ -1,5 +1,7 @@
 import JSONSchemaFaker from "json-schema-faker";
 
+JSONSchemaFaker.option("useExamplesValue", true);
+
 export class Tools {
   constructor({ headers = {} } = {}) {
     this.headers = headers;


Test patch:
diff --git a/test/tools.test.js b/test/tools.test.js
index 4a7ba02..ae59180 100644
--- a/test/tools.test.js
+++ b/test/tools.test.js
@@ -42,4 +42,10 @@ describe("tools", () => {
 
     expect(typeof tools.randomFromSchema({ type: "integer" })).toBe("number");
   });
+
+  it("randomFromSchema() uses examples", () => {
+    const tools = new Tools();
+
+    expect(tools.randomFromSchema({ type: "integer", examples: [5] })).toBe(5);
+  });
 });