DEV Community

Cover image for Power up your (API) mocking game with casual
Eka
Eka

Posted on

Power up your (API) mocking game with casual

Cover image: Calvin & Hobbes © Bill Watterson

Previously in this series, I made a GraphQL API server that returns mock data 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

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 or songs with a very long title.

We are going to use a library called casual for our custom mock data.

GitHub logo boo1ean / casual

Fake data generator for javascript

Fake data generator Build Status

Installation

npm install casual

Usage

var casual = require('casual');
// Generate random sentence
// You don't need function call operator here
// because most of generators use properties mechanism
var sentence = casual.sentence;
// Generate random city name
var city = casual.city;
// Define custom generator
casual.define('point', function() {
    return {
        x: Math.random(),
        y: Math.random()
    };
});

// Generate random point
var point = casual.point;

// And so on..
Enter fullscreen mode Exit fullscreen mode

Casual uses javascript properties for common generators so you don't need to use function call operator

Embedded generators

// Address
casual.country              // 'United Kingdom'
casual.city                 // 'New Ortiz chester'
casual.zip(digits = {5, 9}
Enter fullscreen mode Exit fullscreen mode

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

npm install casual # or yarn add casual
Enter fullscreen mode Exit fullscreen mode

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

const casual = require("casual")
Enter fullscreen mode Exit fullscreen mode

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 so it returns the value generated by casual.

My mocks object now looks like this:

const mocks = {
  Artist: () => {
    return { name: () => "Artist Name" };
  },
  // ...etc
}
Enter fullscreen mode Exit fullscreen mode

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

const mocks = {
  Artist: () => {
-   return { name: () => "Artist Name" };
+   return { name: () => casual.name };
  },
  // ...etc
}
Enter fullscreen mode Exit fullscreen mode

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

"Ms. Timothy Lesch"
"Mrs. Napoleon Hayes"
"Dr. Alisha Cole"
"Mr. Celia Lockman"
Enter fullscreen mode Exit fullscreen mode

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.

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 };
+ },
}
Enter fullscreen mode Exit fullscreen mode
  • casual.date() is a function that returns a date string, eg. "2021-03-25". It takes an optional argument, a momentjs-compatible formatter. 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.

casual.define('artist_name', () => {
  // TODO return something
});
Enter fullscreen mode Exit fullscreen mode

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 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:

const bandNames = [
  "Crystal Greener",
  "The Threshold Renewable",
  "Unevenly Optimist",
  // ... etc
];
Enter fullscreen mode Exit fullscreen mode

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.

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
  ]);
});
Enter fullscreen mode Exit fullscreen mode

Then we use it in our mock resolver function.

Artist: () => {
+  return { name: () => casual.artist_name };
},
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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.

casual.define("artist", () => {
  return {
    name: () => casual.artist_name,
    bio: () => casual.description
  }
})
Enter fullscreen mode Exit fullscreen mode

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:

  • address
  • color
  • date
  • internet
  • payment
  • person
  • misc
  • number
  • text

The full_name generator, for example, is defined in the person provider (source code).

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().

const music_provider = {
  // TODO add functions or properties here
};

casual.register_provider(music_provider);
Enter fullscreen mode Exit fullscreen mode

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

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);
Enter fullscreen mode Exit fullscreen mode

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.

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)
    ])
  },
};
Enter fullscreen mode Exit fullscreen mode

The best way to see what we can do when defining providers is to look at the source code, eg. the address provider.

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

const mocks = {
  Artist: () => {
    return { name: () => casual.artist_name };
  },
  Track: () => {
    return {
      track_number: () => casual.track_number,
      tempo: () => casual.tempo,
    };
  },
}
Enter fullscreen mode Exit fullscreen mode

...

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:

- const casual = require('casual');
+ const casual = require('casual').id_ID;
Enter fullscreen mode Exit fullscreen mode

Locale providers are simply copies of the default providers in directories with the locale names (en_US, fr_FR, id_ID, etc) — see source. 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 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 only has the address provider, so everything else uses the default.

We barely scratched the surface with casual. Check out their docs 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!

Top comments (0)