Testing Code-Native Integrations
Testing a code-native integration
There are two types of testing that you can do with a code-native integration: you can run unit tests of your code locally in your IDE, and you can import the integration and test it in the Prismatic runner.
- Unit tests in your IDE are great for testing the logic of your integration and testing modular portions of your code
- Testing in the Prismatic runner is great for trying out the configuration wizard experience you've built and testing the integration's behavior in a real-world environment.
You will probably want to incorporate both types of testing into your development process.
Testing a code-native integration in Prismatic
After building with npm run build
and importing your code-native integration with prism integrations:import --open
you can test your integration in the Prismatic runner similar to how you test a low-code integration.
To test your config wizard experience, click the Test Configuration button. To run a test of a flow, select the flow from the dropdown menu on the top right of the page and then click the green Run button. When you're satisfied with your integration, you can click Publish to publish a new version of your integration, and manage instances of your integration from the Management tab.
A code-native integration has no steps - just a trigger and onExecution
function, so there's no step results to inspect.
To debug your integration, use the context.logger
object in your onExecution
.
You can even conditionally log lines based on a config variable you create.
You can make the debug config variable invisible to customer users.
configVar({
stableKey: "debug",
dataType: "boolean",
description: "Enable debug logging",
defaultValue: "false",
orgOnly: true,
visibleToCustomerDeployer: false,
});
Unit tests for code-native integrations
You can also write unit tests for your code-native integration, similar to unit tests for custom components.
The invokeFlow
function from the custom component SDK is used to invoke a test of a flow in a code-native integration.
You can specify a sample payload to "send" to your flow's onTrigger
function, and the invokeFlow
function will run both onTrigger
and onExecution
and return the result of the flow's onExecution
function.
Below is a simple flow that takes a payload and sends the payload to an API, returning the results of the API call. The corresponding unit test code invokes the flow, "sending" a sample payload and verifying that the results received are as expected.
- CNI Code
- Unit test code
import {
configPage,
configVar,
flow,
integration,
} from "@prismatic-io/spectral";
import axios from "axios";
const configPages = {
"Acme Config": configPage({
elements: {
"Acme API Endpoint": configVar({
stableKey: "1F886045-27E7-452B-9B44-776863F6A862",
dataType: "string",
description: "The endpoint to fetch TODO items from Acme",
defaultValue:
"https://my-json-server.typicode.com/prismatic-io/placeholder-data/todo",
}),
"Acme API Key": configVar({
stableKey: "webhook-config-endpoint",
dataType: "string",
description:
"The endpoint to call when deploying or deleting an instance",
}),
},
}),
};
export const myFlow = flow({
name: "Create Acme Opportunity",
stableKey: "create-acme-opportunity",
description: "Create an opportunity in Acme",
onExecution: async (context, params) => {
const { id, name, value } = params.onTrigger.results.body.data as {
id: string;
name: string;
value: number;
};
if (value < 0) {
throw new Error("Invalid value - values cannot be negative");
}
const acmeEndpoint = context.configVars["Acme API Endpoint"];
const response = await axios.post(
`${acmeEndpoint}/opportunity`,
{ id, name, value },
{
headers: {
Authorization: `Bearer ${context.configVars["Acme API Key"]}`,
},
},
);
return { data: response.data };
},
});
export default integration({
name: "acme-cni",
description: "Acme CNI",
iconPath: "icon.png",
flows: [myFlow],
configPages,
componentRegistry,
});
import { myFlow } from ".";
import {
invokeFlow,
defaultTriggerPayload,
} from "@prismatic-io/spectral/dist/testing";
interface MyFlowResponse {
externalId: string;
id: string;
name: string;
value: number;
}
describe("test myFlow", () => {
test("Verify that the API returns an external ID that matches the specified ID", async () => {
const { result } = await invokeFlow(
myFlow,
{
"Acme API Endpoint": "https://staging.api.example.com",
"Acme API Key": "my-api-key",
},
{},
{
...defaultTriggerPayload(),
body: {
data: { id: "123", name: "my-opportunity", value: 1000 },
contentType: "application/json",
},
},
);
expect((result?.data as MyFlowResponse).externalId).toBe("123");
});
test("Verify that errors are thrown when provided negative values", async () => {
await expect(
invokeFlow(
myFlow,
{
"Acme API Endpoint": "https://staging.api.example.com",
"Acme API Key": "my-api-key",
},
{},
{
...defaultTriggerPayload(),
body: {
data: { id: "123", name: "my-opportunity", value: -1000 },
contentType: "application/json",
},
},
),
).rejects.toThrow("Invalid value - values cannot be negative");
});
});
You can run a unit test with
npm run test
Note that if your code-native integration depends on existing components' actions, your local environment does not have the necessary component code and you must test your integration within Prismatic.
Unit testing a code-native integration with an OAuth 2.0 connection
If your integration includes an OAuth 2.0 connection, you can use the same strategy outlined in the Unit Testing Custom Components guide.
Both custom components and code-native integrations can take advantage of the prism components:dev:run
command to fetch an established connection from an existing test instance.