Writing Unit Tests for Custom Components

This post refers to an earlier version of Prismatic. Check out our quickstart on unit testing custom components or contact us if you'd like help with any of the topics addressed in this post.
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:
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:
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:
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 integration platform for B2B software companies. It's the quickest way to build integrations to the other apps your customers use and to add a native integration marketplace to your product. A complete embedded iPaaS solution that empowers your whole organization, Prismatic encompasses an intuitive integration designer, embedded integration marketplace, integration deployment and support, and a purpose-built cloud infrastructure. Prismatic was built in a way developers love and provides the tools to make it perfectly fit the way you build software.
Get the latest from Prismatic
Subscribe to receive updates, product news, blog posts, and more.