Prismatic is currently in private beta! Request beta access
Photo of Taylor Reece
Taylor Reece, Developer Advocate
August 27, 2020 • 6 min read

Writing Unit Tests for Custom Components

Any time I speak to developers about Prismatic, the question is inevitably (and rightly!) asked: "How will this fit into my current development pipeline?" That's a great question to ask, and one that was in the forefront of our minds as we designed our integration platform. A development tool is pretty useless if it doesn't fit into your existing development ecosystem, so we've worked hard to make sure that the custom component development experience slots in nicely with your other development processes.

Today I'd like to talk about one facet of the development process: unit testing your Prismatic custom components. I imagine I'm preaching to the choir, but it's obviously incredibly important to catch errors and breaking changes before they reach production. A good set of unit tests helps you automatically catch and reject bad code changes before they wreak havoc on customers' integrations. By creating a series of tests for your custom components, your development team can feel comfortable updating custom component code without the risk of introducing regressions. Plus, when you write unit tests you're creating sample use cases for your component, which yields documentation-as-code for your custom component.

Let's work through adding unit tests to a sample custom component. We'll first add the appropriate development dependencies to our component package, and then walk through simulating invocations of our component's actions with a couple of tests, so we can catch regressions if they occur in the future.

Adding Unit Tests to Our Unit Converter Component

We developed a "unit converter" component in a previous blog post. TL;DR: a rocket fuel type (like "Kerosene") and amount (like "100 pounds") are passed in as inputs to our component, and the component does some computations and returns the equivalent amount of fuel in gallons. In this example, 100 pounds of kerosene is equivalent to about 14.97 gallons:

Screenshot of Prismatic Integration Designer

Full source code for the unit converter component is available in our GitHub examples repo.

Our component is fine for our current use cases. A conversion table for transforming pounds to gallons (and vice-versa) is currently hard-coded into our component and supports four types of fuel, but in the future we may want to expand functionality of our component by converting to other units, or sourcing the conversion table from an external source that includes more types of fuel. Those are major changes, and we definitely don't want to introduce regressions to our component when we make those changes. We also shouldn't fear making changes to our component code. So, let's should add some tests now to automatically prevent issues in the future. We want to make sure that every time we enter "Kerosene" and "100" into our component, we get approximately "14.97" back out.

Adding Jest to Our Project

Let's start our work by adding Jest with TypeScript support to our project. There are lots of JavaScript testing frameworks, but Jest is well documented, robust, easy to use, and by far the most popular, so let's add it as a dev dependency:

npm install --save-dev jest ts-jest
# or
yarn add --dev jest ts-jest

Next, we'll need to create a jest.config.js config file to let Jest know that we're working with TypeScript:

module.exports = {
  preset: "ts-jest",
  testEnvironment: "node",
};

Finally, let's add some scripts to our package.json file so we can simply run yarn test to invoke unit testing:

"scripts": {
  "format": "prettier --loglevel error --write './src/**/*.{js,jsx,ts,tsx,json}' ./*.{js,json}",
  "build": "webpack",
  "test": "jest"
}

Adding the Unit Tests

Now that we have the prerequisite configuration in place, let's write some Jest tests. All of our component source code is in src/index.ts. Let's create a corresponding test file, src/index.test.ts. I'll throw the entire test source code here, and then we'll parse through it line by line:

import { gallonsToPoundsAction, poundsToGallonsAction } from ".";
import { PerformDataReturn } from "@prismatic-io/spectral";

test("Convert pounds to gallons", async () => {
  const output = (await poundsToGallonsAction.poundsToGallons.perform(null, {
    fuelType: "Kerosene",
    fuelAmount: 100,
  })) as PerformDataReturn;
  expect(output.data).toBeCloseTo(14.97006, 5);
});

test("Convert gallons to pounds", async () => {
  const output = (await gallonsToPoundsAction.gallonsToPounds.perform(null, {
    fuelType: "Hydrazine",
    fuelAmount: 50,
  })) as PerformDataReturn;
  expect(output.data).toBeCloseTo(419.0, 5);
});

The first line imports our component's two actions from src/index.ts, and the second line imports a TypeScript type definition from Prismatic's Spectral library so we can cast our actions' outputs to an object and verify that the object contains correct return values:

import { gallonsToPoundsAction, poundsToGallonsAction } from ".";
import { PerformDataReturn } from "@prismatic-io/spectral";

The next portion outlines a test that we'd like to run. A Jest test() function takes two arguments: the name of the test - "Convert pounds to gallons" - and the test function to execute:

test("Convert pounds to gallons", async () => {

Next, we invoke our poundsToGallonsAction action's asynchronous perform function. This is the function that is invoked when an integration is run.

A perform function takes two parameters: context which contains credentials, config variables and a logger, and params which contains input parameters that are passed into the action. Since our action doesn't take advantage of the credentials, config variables, or the logger, we can just pass null in for the context parameter.

We then pass in an object containing fuelType and fuelAmount as params, and save the return value of our function to a variable named output:

const output = (await poundsToGallonsAction.poundsToGallons.perform(null, {
  fuelType: "Kerosene",
  fuelAmount: 100,
})) as PerformDataReturn;

Finally, we can use Jest's expect and toBeCloseTo functions to verify that the output that our action provided was (with some acceptable rounding error) equal to about 14.97 gallons:

expect(output.data).toBeCloseTo(14.97006, 5);

The next several lines of code outline a similar test for converting gallons to pounds.

After writing our tests, we can run yarn test to invoke Jest and verify that our actions' tests pass:

Screenshot of unit test passing in Prismatic

If someone later commits a change to our component that causes a regression, our build pipeline will run yarn test and reject the committed code with an explanation of why the tests failed:

Screenshot of unit test failing in Prismatic

That's it! Within minutes we have working unit testing that slots nicely into our existing build pipeline. We were already running yarn build to build our component. Our CI/CD script just needs a yarn test line added to it, and it'll begin to catch errors before they reach production.

Further Reading

In this post we walked through adding unit testing to a custom component that we'd previously written. For more details on writing custom components, check out our docs, or turn to our quickstart on building advanced components that handle credentials and binary data. For more information on Jest, check out their getting started guide - Jest has a low learning curve and a ton of great mocking features that can be used in Prismatic custom component unit testing.


About Prismatic

Prismatic is the dev-first integration platform for B2B software companies and the easiest way to build, deploy, and support integrations. A complete toolkit for the whole organization, Prismatic includes an integration designer, testing framework, customer deployment management, logging, monitoring, alerting, and an embeddable customer integration portal. Prismatic is a solution for the real world, designed to handle messy, complex integration scenarios and work with existing toolchains. Flexible and extensible, Prismatic empowers teams to tackle bespoke and vertical-specific integrations between applications of all kinds, SaaS or legacy, with or without a modern API, regardless of protocol or data format. Born out of its founders’ experience scaling a software company with hundreds of unique integrations, Prismatic aims to help teams spend less time on integrations and more time driving core product innovation. Learn more at prismatic.io.

Get the latest from Prismatic

Subscribe to receive updates, blog posts, and more. You'll be the first to know when we launch!

You can unsubscribe at any time.