How to use ConfigCat's public API to conduct integration tests
Tags: featuremanagement, featureflags, testing, configcat
Published: 2023-08-12 15:23:04

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.

<!-- truncate -->

### Prerequisites

- Have [JEST](https://jestjs.io/docs/getting-started) installed - A testing framework for testing your JavaScript code
- Have [Postman](https://www.postman.com/) 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](https://configcat.com/docs/sdk-reference/overview/), you may want to perform the same actions as you normally do while working with the [Dashboard UI](https://app.configcat.com) in your code. This is where the [ConfigCat Public Management API](https://api.configcat.com/docs/) 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](https://jestjs.io/). If you're eager to peek at the API docs before we get started, I have linked it [here](https://configcat.com/docs/advanced/public-api/).

## 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](https://github.com/configcat-labs/public-api-guide-integration-testing-sample). If you wish to code along with me, you can check out the [starter-code branch](https://github.com/configcat-labs/public-api-guide-integration-testing-sample/tree/starter-code). 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](https://axios-http.com/docs/intro). [ConfigCat's Public Management API](https://api.configcat.com/docs/) will only process HTTP requests that contain a [Basic HTTP Authentication Scheme](https://configcat.com/docs/advanced/public-api/#authentication), 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](https://app.configcat.com/my-account/public-api-credentials).

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.

```js
// 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.*

2. In the `utils/feature/feature-integration.js` file, import the *axiosInstance*.

```js
const axiosInstance = require('../axios/axios-instance');
```

3. After reviewing and looking at the examples in the [API docs](https://api.configcat.com/docs/), I wrote some individual functions that set up the necessary testing resources. Add these to `utils/feature/feature-integration.js`:

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

4. I'll bundle the individual functions together in a function called `setupFeatureIntegration`:

```js
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](https://github.com/configcat-labs/public-api-guide-integration-testing-sample/blob/master/feature-integration.test.js)

5. 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:

```js
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:

```js
// 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);
 })

```

6. I'll run the test with the `npm run test` command, which will print the test results to the console:

```sh
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.
```

## 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](https://api.configcat.com/docs/). If you're excited about trying this, I highly recommend signing up for a [free tier ConfigCat account](https://app.configcat.com/signup).

For more awesome posts and other announcements, follow ConfigCat on [Twitter](https://twitter.com/configcat), [Facebook](https://www.facebook.com/configcat), [LinkedIn](https://www.linkedin.com/company/configcat/), and [GitHub](https://github.com/configcat). The most lovable feature flag service, ever.