Integration Testing with ConfigCat's Public API
Feature flags are essential for effective feature release and management. Using them, we can control what features end users can see and which should remain hidden. Feature flagging allows developers to plan, launch and test new features remotely without editing code. While these benefits are fantastic, what about code testing? Having some methods in place for testing the integration of feature flags in our code can increase the likelihood of smooth feature integrations.
Prerequisites
- Have JEST installed - A testing framework for testing your JavaScript code
- Have Postman installed - A platform used for testing and documenting APIs
- Basic JavaScript and Testing knowledge
ConfigCat's public management API
Besides evaluating your feature flags with ConfigCat's SDKs, you may want to perform the same actions as you normally do while working with the Dashboard UI in your code. This is where the ConfigCat Public Management API comes in useful. And to show you how it works, I'll demo how we can use it to perform actions such as setting up a testing environment and creating a new feature flag using JavaScript. Then finally, we'll write an integration test to ensure all the functions work with each other using the JEST JavaScript Testing Framework. If you're eager to peek at the API docs before we get started, I have linked it here.
What is integration testing?
Compared to a unit test, which only tests a single piece of code or function, an integration test involves testing multiple units to determine if they work together.
Imagine you want to test if a feature flag integrates well with your app before performing a feature deployment. A recommended way to do this is to write an integration test to catch integration bugs that could potentially seep into production.
Writing an integration test
Let's put the theory aside for integration tests and write one in JavaScript. Before starting, let's write some functions that utilize the public API to create resources that our testing function will evaluate.
To help you follow along, I've added the following sample app. If you wish to code along with me, you can check out the starter-code branch. If you end up getting stuck, feel free to view the complete code on the main
branch.
Because this process involves making HTTP calls to the API, I'll use an HTTP client called Axios. ConfigCat's Public Management API will only process HTTP requests that contain a Basic HTTP Authentication Scheme, so I'll add a basic auth username and password to the HTTP request header.
-
Create your public API credentials on the Public API credentials management page.
-
I'll create an Axios instance using the credentials. This will allow you to make requests without supplying the required headers each time. Add the following code and input your credentials in the
utils/axios/axios-instance.js
file.
// axios-instance.js
const axios = require('axios');
const axiosInstance = axios.create({
baseURL: 'https://api.configcat.com/v1/',
headers: {
'X-CONFIGCAT-SDKKEY': 'YOUR-CONFIGCAT-SDKKEY'
},
auth: {
username: 'YOUR-AUTH-USERNAME',
password: 'YOUR-AUTH-PASSWORD'
}
});
module.exports = axiosInstance;
Do this only to follow me. In a production environment keep your credentials secure: do not embed them directly in your code and do not share them.
- In the
utils/feature/feature-integration.js
file, import the axiosInstance.
const axiosInstance = require('../axios/axios-instance');
- After reviewing and looking at the examples in the API docs, I wrote some individual functions that set up the necessary testing resources. Add these to
utils/feature/feature-integration.js
:
// Create Test Product
const createTestProduct = async () => {
const organizationId = "08da0156-fc5b-4132-88e5-1d42032078f5";
return await axiosInstance
.post(`organizations/${organizationId}/products`, {
name: "Test Product",
description: "A product for testing",
})
.then((response) => response.data)
.catch((error) => {
// throw new Error(error);
console.log(error);
});
};
// Create Test Environment
const createTestEnvironment = async (productId) => {
return await axiosInstance
.post(`products/${productId}/environments`, {
name: "Test Environment",
description: "An environment for testing",
})
.then((response) => response.data)
.catch((error) => {
throw new Error(error);
});
};
// Create Test Config
const createTestConfig = async (productId) => {
return await axiosInstance
.post(`products/${productId}/configs`, {
name: "Test Config",
description: "A config for testing",
})
.then((response) => response.data)
.catch((error) => {
throw new Error(error);
});
};
// Create Test Feature Flag
const createTestFeatureFlag = async (configId, environmentId) => {
return await axiosInstance
.post(`configs/${configId}/settings`, {
hint: "A feature flag for testing",
key: "testfeatureflag",
name: "Test Feature Flag",
settingType: "boolean",
initialValues: [
{
environmentId: environmentId,
value: true,
},
],
})
.then((response) => response.data)
.catch((error) => {
throw new Error(error);
});
};
- I'll bundle the individual functions together in a function called
setupFeatureIntegration
:
async function setupFeatureIntegration() {
try {
const { productId } = await createTestProduct();
const { configId } = await createTestConfig(productId);
const { environmentId } = await createTestEnvironment(productId);
const createTestFeatureFlagResponse = await createTestFeatureFlag(configId, environmentId);
createTestFeatureFlagResponse.productId = productId;
return createTestFeatureFlagResponse;
} catch (error) {
throw new Error(error);
}
}
I've added the productId
variable to the response object returned from the createTestFeatureFlag
function to delete the Test Product
when the test completes. Deleting a product will remove all its' child resources like Environments, Configs, and Feature Flags.
You can find the complete code here
- Imagine for a moment that you have a function like this in your app that depends on a specific return value produced by the
setupFeatureIntegration
function:
function appFunctionThatUsesTheFeatureFlag(featureFlagKey) {
if (featureFlagKey) {
// I can use the feature flag key
} else {
// I can not use the feature flag key... handle error
}
}
Let's write a simple integration test that expects the feature key to be returned from the setupFeatureIntegration
function. The test function checks that the response object contains a property key. If it doesn't, then the test will fail. In the feature-integration.test.js
file, add the following code:
// feature-integration.test.js
const axiosInstance = require('./utils/axios/axios-instance');
const setupFeatureIntegration = require('./utils/feature/feature-integration');
// Remove Test Resources
const removeTestResources = async (productId) => {
return await axiosInstance
.delete(`products/${productId}`)
.then(() => console.log("Testing resources removed..."))
.catch((error) => {
throw new Error(error);
});
};
test('should return an object containing a property key', async () => {
const data = await setupFeatureIntegration();
expect(data).toHaveProperty("key");
const { productId } = data;
await removeTestResources(productId);
})
- I'll run the test with the
npm run test
command, which will print the test results to the console:
chavez@Chavezs-MBP public-api-guide-integration-testing-sample % npm run test
> [email protected] test
> jest
console.log
Testing resources removed...
at log (feature-integration.test.js:8:25)
PASS ./feature-integration.test.js
✓ should return an object containing a property key (3301 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.578 s
Ran all test suites.
Conclusion
Integration tests allow us to uncover and fix integration bugs. It comes in handy for checking if individual units of code function together to achieve specific functionality. I've also discussed and demonstrated how to use it to test the integration of a feature flag in some JavaScript code using ConfigCat's Public Management API. If you're excited about trying this, I highly recommend signing up for a free tier ConfigCat account.
For more awesome posts and other announcements, follow ConfigCat on Twitter, Facebook, LinkedIn, and GitHub. The most lovable feature flag service, ever.