DEV Community

Cover image for How to use ConfigCat's public API to conduct integration tests
Chavez Harris
Chavez Harris

Posted on • Originally published at configcat.com

How to use ConfigCat's public API to conduct integration tests

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.

  1. Create your public API credentials on the Public API credentials management page.

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

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.

  1. In the utils/feature/feature-integration.js file, import the axiosInstance.
const axiosInstance = require('../axios/axios-instance');
Enter fullscreen mode Exit fullscreen mode
  1. 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);
    });
};
Enter fullscreen mode Exit fullscreen mode
  1. 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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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

  1. 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
  }
}
Enter fullscreen mode Exit fullscreen mode

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);
 })

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

> public-api-guide-integration-testing-sample@1.0.0 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.
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)