Power up your (API) mocking game with casual
Tags: graphql, casual, api
Published: 2021-03-27 07:12:01

<figcaption>Cover image: Calvin & Hobbes &copy; Bill Watterson</figcaption>

---

Previously in this series, I made a [GraphQL API server that returns mock data](https://dev.to/ekafyi/typing-and-mocking-a-graphql-api-server-with-apollo-3f4h) for a (nonexistent) app that displays my favourite music releases.

Having mock data facilitates our API server's integration with other parts of design and development work, such as UI development, prototyping, and testing, even while the server's resolvers are still under development.

An example query and mock data from the previous post:

![GraphQL IDE showing query and JSON data of tracks and albums](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1asiae42aqmuj0cyh0zd.png)

However, this mock data could do with some improvements, for example:
- The `track_number` field—indicating a song/track’s order in an album—could return a negative integer (impossible) or a very large one (how many albums do you know contains 91 tracks??).
- All albums, tracks, and artists have the same mock names, *"Album/Track Title"* and *Artist Name*. We want to develop and test the UI with varying data, including artists with a [very long name](https://open.spotify.com/artist/6wBUn8gMP85n8dPu6LoUcF) or songs with a [very long title](https://open.spotify.com/track/56QCsnXzOlh3oSdPhrOjhO).

We are going to use a library called [casual](https://www.npmjs.com/package/casual) for our custom mock data.

{% github boo1ean/casual %}

## Table of Contents

1. Installation
2. Basic usage
3. More basic usage examples
4. Custom generators with casual.define
5. Custom providers with casual.register_provider
6. Localization

## 1. Installation

```bash
npm install casual # or yarn add casual
```

Import/require casual in your server app main script (eg. `index.js` or `app.js`).

```js
const casual = require("casual")
```

## 2. Basic usage

Casual comes with dozens of built-in generators (in the form of properties and functions) that return dummy data for various common purposes such as user data, addresses, numbers, dates, copy text, etc.

In an Apollo GraphQL server, we pass a casual generator to our [custom mocks object](https://www.apollographql.com/docs/apollo-server/testing/mocking/#customizing-mocks) so it returns the value generated by casual.

My mocks object now looks like this:

```js
const mocks = {
  Artist: () => {
    return { name: () => "Artist Name" };
  },
  // ...etc
}
```

Let's replace the dummy text *"Artist Name"* with `casual.name`.

```diff
const mocks = {
  Artist: () => {
-   return { name: () => "Artist Name" };
+   return { name: () => casual.name };
  },
  // ...etc
}
```

The server now returns mock artist names such as...

```
"Ms. Timothy Lesch"
"Mrs. Napoleon Hayes"
"Dr. Alisha Cole"
"Mr. Celia Lockman"
```

Unusual artist names for sure 😁, but at least they are not all *"Artist Name* anymore.

## 3. More basic examples

Now let’s try other simple built-in generators.

```diff
const mocks = {
  Artist: () => {
    return { name: () => casual.name };
  },
  Album: () => {
    return {
      // ... other fields
+     release_date: () => casual.date()
    };
  },
  Track: () => {
    return {
      // ... other fields
+     track_number: () => casual.integer(1, 12)
    };
  },
+ ExternalUrlObject: () => {
+   return { spotify: () => casual.url };
+ },
}
```

- `casual.date()` is a function that returns a date string, eg. `"2021-03-25"`. It takes an optional argument, a [momentjs-compatible formatter](http://momentjs.com/docs/#/parsing/string-format/). If no argument is passed, it uses the default format `'YYYY-MM-DD'`.
- `casual.integer(1,12)` is a function that returns an integer between `1` and `12`, thus solving our track number predicament. (Yes, we could roll our own random integer generator without a library, but this is helpful in terms of speed.)
- `casual.url` is a property that returns an URL, eg. `'http://www.Germaine.net/'`. For some reason, the domain name is always capitalized, so I add `.toLowerCase()` in my full code.

#### Combining generators

We can also combine generator functions. For instance: 
- `casual.words(5)` returns five random words
- `casual.integer(1,6)` returns a random integer between `1` and `6`

We can use `casual.words(casual.integer(1, 6))` to return *a phrase that consists of one to six words*. This gives us a (slightly) better control of the text length compared to generators like `title`, `text`, and `description`.

## 4. Custom generators with casual.define

We can define custom generators with `casual.define(name, fn)`. The first argument is the generator name and the second is a function returning the value.

Let's make a custom `artist_name` generator.

```js
casual.define('artist_name', () => {
  // TODO return something
});
```

I want the mock artist name to return either *a person’s name* (representing solo artists) or *a group/band name*. I’ve got a list of fictional band names from [Band Name Generator](https://www.indiesound.com/band-name-generator/) for the latter, and for the person names I'm simply using the `casual.full_name` generator.

I define the band names in a hardcoded array:

```js
const bandNames = [
  "Crystal Greener",
  "The Threshold Renewable",
  "Unevenly Optimist",
  // ... etc
];
```

Next I’m going to: (1) pick one name out of the `bandNames` array, and (2) randomize returning either a person name or a band name. The `casual.random_element()` helper, which returns an item from the array passed as argument, is perfect for those.

```js
casual.define("artist_name", () => {
  // pass array containing a person full_name and a band name to return at random
  return casual.random_element([
    casual.full_name,
    casual.random_element(bandNames) // get a random band name
  ]);
});
```

Then we use it in our mock resolver function.

```diff
Artist: () => {
+  return { name: () => casual.artist_name };
},
```

When we run our query, now we get a list of more realistic artist names. 🎉

```
"Price Tillman"
"The Out Garter"
"Colby Bergnaum"
"The Threshold Renewable"
"Raphael Kohler"
"The Fountains Altars"
```

We can go beyond an individual field (eg. name) and define an object type in the same way, for example define a `casual.artist` generator that returns an object with `name` and `bio` properties. Make sure the object shape matches the type definition, though.

```js
casual.define("artist", () => {
  return {
  	name: () => casual.artist_name,
	bio: () => casual.description
  }
})
```

## 5. Custom providers with casual.register_provider

Under the hood, casual generators are simply functions in a **provider object**. Casual includes the following [built-in providers](https://github.com/boo1ean/casual/tree/master/src/providers):
- address
- color
- date
- internet
- payment
- person
- misc
- number
- text

The `full_name` generator, for example, is defined in the `person` provider ([source code](https://github.com/boo1ean/casual/blob/master/src/providers/person.js#L49-L51)).

`casual.define` is sufficient for one-off individual generators, but if we have an object type with multiple fields/properties to mock up, we can **register our own custom providers**.

For example, a music track would not only have a name/title but also track number, tempo in BPM (beats per minute), and duration in millisecond. Let's try registering a custom `music_provider`. First we create an object and pass it to   `casual.register_provider()`.

```js
const music_provider = {
  // TODO add functions or properties here
};
 
casual.register_provider(music_provider);
```

Then we add the generator functions as the object properties. For example, we're adding `track_number` and `tempo` to our music provider.

```js
const music_provider = {
  // Track number in an album, between 1 to 12
  track_number: () => casual.integer(1, 12),

  // Song tempo in BPM, between 60 to 180
  tempo: () => casual.integer(60, 180)
};

casual.register_provider(music_provider);
```

Since these are regular functions, we can define our generator to accept arguments. For example, we can make an `artist_name` generator that accepts a `type` argument of `"PERSON"` or `"GROUP"`. If no argument is provided, we return any of those at random.

```js
const bandNames = [ ... ]; // same data as Step 4

const music_provider = {
  // ... other generators

  // Return an artist name. Name can be a solo artist's name ("PERSON") or a band name ("GROUP").
  artist_name: (type) => {
    if (type === "PERSON") return casual.full_name;
    else if (type === "GROUP") return casual.random_element(bandNames);
    return casual.random_element([
      casual.full_name,
      casual.random_element(bandNames)
    ])
  },
};
```

The best way to see what we can do when defining providers is to look at the source code, eg. [the address provider](https://github.com/boo1ean/casual/blob/master/src/providers/address.js).

To use the generators from our custom provider, we call them the same way as built-in or custom defined ones. 

```js
const mocks = {
  Artist: () => {
    return { name: () => casual.artist_name };
  },
  Track: () => {
    return {
	  track_number: () => casual.track_number,
	  tempo: () => casual.tempo,
    };
  },
}
```

...

## 6. Localization

Casual comes with localization built-in. Simply add the locale name when calling the default import. For example, to get the Indonesian locale:

```diff
- const casual = require('casual');
+ const casual = require('casual').id_ID;
```

Locale providers are simply copies of the default providers in directories with the locale names (`en_US`, `fr_FR`, `id_ID`, etc) — [see source](https://github.com/boo1ean/casual/tree/master/src/providers). These locale providers are submitted by contributors.

`<digression>`
Off topic but interesting (to me): While looking at the locale providers, I learned that [Russian female last names](https://github.com/boo1ean/casual/blob/master/src/providers/ru_RU/person.js#L134-L136) are the male last names + an `a` in the end. I went on to Google the tennis athlete *Anna Kournikova* and sure enough, her father's name is *Kournikov*.
`</digression>`

When a localized provider is not available, it falls back to the default (English). For instance, the [Indonesian locale](https://github.com/boo1ean/casual/tree/master/src/providers/id_ID) only has the `address` provider, so everything else uses the default.

We barely scratched the surface with casual. Check out [their docs](https://github.com/boo1ean/casual#readme) to see all available functionalities.

---

Enough mocking for now—in the next post I'm going to start learning about GraphQL server resolvers. Thank you for reading!