Skip to main content

Testing

When integration testing or running your systems within a deployed environment, we recommend that you use a dedicated Basis Theory tenant for each test environment that is separate from your production tenant. For example, if you maintain a separate Development, QA, Staging, and Production environment for your systems, we recommend that you mirror this setup by creating 4 separate tenants in Basis Theory. This can help you to isolate test data from production data and allow you to more securely lock down access to production API keys.

Testing Reactors

Reactor code is simply an ES module that exports a default function. This can be tested like any JavaScript code or ES module using the tooling of your choice. Internally at Basis Theory, we have automated tests for our reactor code using Jest to ensure the JavaScript code itself works as expected.

Unit Testing

For unit testing, we use Jest mocks to mock either SDK methods if we use a third party SDK in the reactor, or to mock usages of an http client (e.g., axios, node-fetch).

const fetch = require('node-fetch');

// this is a fake piece of data we expect the destination service to return
const fakeTransactionId = chance.string();

// here we mock the json response returned by node-fetch
jest.mock('node-fetch', () =>
jest.fn().mockResolvedValue({
status: 200,
json: jest
.fn()
// this mock should return a "real" response based on what your mocked service normally returns
.mockImplementation(() => Promise.resolve({ transaction_id: fakeTransactionId }))
})
);

describe('Example reactor unit test', () => {
const reactorFunction = require('./reactor');

it('should call third party service', async () => {
// this is the request we will pass into the reactor function
const reactorRequest = {
configuration: {
THIRD_PARTY_API_KEY: chance.string()
},
args: {
card_number: chance.string()
}
};

// we expect the third party service to be called by our reactor with this payload
const expectedRequestToThirdParty = {
payment_method: {
credit_card: {
number: reactorRequest.args.card_number
}
}
};

const reactorResponse = await reactorFunction(reactorRequest);

expect(fetch).toHaveBeenCalledTimes(1);
expect(fetch).toHaveBeenCalledWith(
'https://my.downstream.service.com/',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
// our third party service accepts the API key in an X-API-KEY header
'X-API-KEY': reactorRequest.configuration.THIRD_PARTY_API_KEY
},
body: JSON.stringify(expectedRequestToThirdParty)
}
);

// we expect our reactor to return the transactionId returned by the third party service
expect(reactorResponse).toEqual({
raw: {
transactionId: fakeTransactionId
}
});
});
});

Integration Testing

For integration testing, we ensure the reactor code actually works against the destination services that the reactor integrates with. As an example, the following snippet shows a sample reactor integration test:

const reactorFunction = require('./reactor'); // this file exports our reactor function as an ES module
const { AuthenticationError } = require('@basis-theory/basis-theory-reactor-formulas-sdk-js');

describe('Example reactor integration test', () => {
it('should return expected response', async () => {
const actualResponse = await reactorFunction({
configuration: {
// this is a real api key for an external service called by our reactor
THIRD_PARTY_API_KEY: process.env.THIRD_PARTY_API_KEY
},
args: {
// requests to the reactor will contain a token, which will be detokenized
// into plaintext before it reaches our reactor code
// here we test with a fake plaintext card number
card_number: '4242424242424242'
}
});

// assert on whatever you expect the reactor to return, in our case we expect a transactionId to be returned
expect(actualResponse.raw.transactionId).not.toBeNull();
});

it('should throw an AuthenticationError when using an invalid API key', async () => {
await expect(
reactorFunction({
configuration: {
// pass in a fake api key that will be rejected by the third party service
THIRD_PARTY_API_KEY: chance.string()
},
args: {
card_number: '4242424242424242'
}
})
).rejects.toThrow(new AuthenticationError().message);
});
});

End-to-End Testing

End-to-end tests against reactors running in Basis Theory can provide further confidence that your reactors work in a deployed environment. These tests require a bit more setup and should sit at the top of your testing pyramid, so we encourage you to first focus on unit and integration tests before attempting to write automated end-to-end tests. If you do choose to write end-to-end tests, here are some tips to keep in mind:

  • The reactor under test must be provisioned either just-in-time during the test using a management application's API key, or manually provisioned ahead of time and referenced by id in the tests. Either solution requires some configuration to be passed into your tests.
  • Invoking your reactor under test will require a private application's API key with token:use permission.
  • If your reactor expects to be called with detokenization expressions in the request, the tokens referenced in your test requests must be pre-created by your test code. This will require access to either a public or private application's API key with token:create permission.

Testing Techniques

Mocking at the Network Level

Mocking network calls is useful within a category of testing that we refer to as Acceptance Testing. While the terminology differs throughout the software industry, we use this term to refer to black box testing of a system component to ensure it meets business requirements (i.e. acceptance criteria). With this type of test, you typically strive to mock external dependencies, especially network calls to external systems.

There are many tools available to facilitate mocking network calls, such as Wiremock (our preferred tool) or Mock Service Worker. If you're interested in learning more, check out our series of blog posts diving deep into our testing philosophy at Basis Theory.

Mocking at the Unit Level

We recommend that you generally adhere to good design principles and strive to loosely couple your application code from external dependencies, including integration points with Basis Theory. This can be accomplished by using inversion of control and dependency injection within your codebase whenever possible.

We also offer official SDKs in many languages, which provide standard interfaces for interacting with the Basis Theory API. This makes it easy to mock methods on these interfaces within your unit tests. While the specifics of how to structure your code and mock dependencies within unit tests differ across languages and frameworks, you have all the tools available to write loosely-coupled and well-tested code.

Test Data

Card Numbers

The card and card_number token types accept any Luhn-valid card numbers and are not restricted to a particular set of card numbers, even for tenants only used for testing purposes. However, if you exchange card tokens with any external systems using reactors or the proxy, those systems may have their own test card requirements that you should follow to ensure the integration works as expected.

BIN Details Test Cards

Your Test Tenant can be configured to return fake data for the BIN Details enrichment and can be requested in the Tenant's Quota Page.

When fake responses are enabled the following will occur:

  1. If a card does not have a static value below, on every request a random set of BIN details following the enhanced data structure.
  2. If a card does have a static value below, on every request the BIN details will be the same.

The following card numbers ranges have static BIN Details:

All the 3DS Test Cards are included in the fake BIN database as CREDIT cards.
Card Number BeginCard Number EndTypeCard BrandTest Card Example
42424242424242424242424242424242CREDITVISA4242424242424242
55555555555500005555555555560000CREDITMASTERCARD5555555555554444
378282246310000378282246310010CREDITAMEX378282246310005
40000566556655504000056655665550CREDITVISA4000056655665556
62000000000000006200000000000000CREDITUNIONPAY6200000000000005
60110100000000006011010000000005DEBITDISCOVER6011010000000003
35660020203605043566002020360506DEBITJCB3566002020360505
378282246310011378282246310020DEBITVISA378282246310013
40055500000810184005550000081020DEBITVISA4005550000081019
51120004000000005112000400000001DEBITMASTERCARD5112000400000000
51120100000000035112010100000002DEBITMASTERCARD5112010000000003
60110009901394206011000990139430CREDITDISCOVER6011000990139424
60111111111111106011111111111120CREDITDISCOVER6011111111111117

3DS Test Cards

The following test card numbers can be used to test various 3D Secure scenarios.

Card NumberCard Brand3DS ScenarioIs Luhn Valid
4200000000000002VISASuccessful Frictionless AuthenticationNo
4200000000000003VISAAuthentication AttemptedNo
4200000000000005VISAAuthentication FailedNo
4200000000000006VISAAuthentication UnavailableNo
4200000000000007VISAAuthentication RejectedNo
4200000000000004VISASuccessful Challenge AuthenticationNo
4200000000000014VISASuccessful Challenge Authentication - Method not RequiredNo
4200000000000015VISASuccessful Mandated Challenge AuthenticationNo
4200000000000016VISASuccessful Out-of-Band Challenge AuthenticationNo
4200000000000008VISAAttempted Challenge AuthenticationNo
4200000000000009VISAFailed Challenge AuthenticationNo
4200000000000017VISAFailed Out of Band Challenge AuthenticationNo
4200000000000010VISAUnavailable Challenge AuthenticationNo
4200000000000011VISARejected Challenge AuthenticationNo
4200000000000012VISA3DS Directory Server ErrorNo
4200000000000013VISAInternal 3DS Server ErrorNo
4264281511112228VISAAuthentication FailedYes
340000000004001AMEXSuccessful Challenge AuthenticationYes
4000020000000000VISASuccessful Challenge AuthenticationYes
4111111111111111VISAAuthentication AttemptedYes
5204247750001471MASTERCARDSuccessful Frictionless AuthenticationYes
6011601160116011DISCOVERSuccessful Frictionless AuthenticationYes
370000000000002AMEXSuccessful Challenge AuthenticationYes
3566002020360505JCBSuccessful Challenge AuthenticationYes
3566006663297692JCBSuccessful Challenge AuthenticationYes
36185973325993DISCOVERSuccessful Challenge AuthenticationYes
5424180011113336MASTERCARDAuthentication AttemptedYes
5424180000000171MASTERCARDAuthentication FailedYes
5405001111111165MASTERCARDAuthentication UnavailableYes
5405001111111116MASTERCARDAuthentication RejectedYes
4005562231212123VISASuccessful Challenge Authentication - Method not RequiredYes
4761369980320253VISASuccessful Mandated Challenge AuthenticationYes
4000000000000341VISASuccessful Out-of-Band Challenge AuthenticationYes
5200000000001104MASTERCARDSuccessful Mandated Challenge AuthenticationYes
4005571701111111VISAAttempted Challenge AuthenticationYes
4055011111111111VISAFailed Challenge AuthenticationYes
5427660064241339MASTERCARDFailed Challenge AuthenticationYes
6011361011110004DISCOVERFailed Out of Band Challenge AuthenticationYes
6011361000008888DISCOVERUnavailable Challenge AuthenticationYes
6011361000001115DISCOVERRejected Challenge AuthenticationYes
4264281500003339VISA3DS Directory Server ErrorYes
4264281500001119VISAInternal 3DS Server ErrorYes
5424180011110001MASTERCARD3DS Directory Server ErrorYes

Account Updater Test Cards

The following test card numbers can be used to test various Account Updater scenarios.

PANExp. MonthExp. YearResult CodeUpdate Details
4111111111111111122023UPD_PAN4166676667666746
6011690151507086122023UPD_EXP_DATE12/2026
6011760519541711122023UPD_BRAND_CONVN/A
6011490740263725122023UPD_CORRECTEDN/A
5461310156953048122023WRN_CLOSED_ACCOUNTN/A
4929980395567582122023WRN_CONTACT_CARDHOLDERN/A
4916725297925395122023WRN_ISSUER_NO_DATAN/A
5580422612666704122023WRN_ISSUER_NOT_ENROLLEDN/A
4035501000000008122023WRN_OPT_OUTN/A
201400000000009122023WRN_UNSUPPORTED_NETWORKN/A
6011178332216017122023ERR_UNDEFINEDN/A
6011648103759866122023ERR_INVALID_EXP_DATEN/A
378025849667382122023ERR_INVALID_PANN/A
370000000000002122023ERR_INVALID_CONFIGN/A
4711358892785746122023(No change and not returned in file)N/A

Bank Verification Test Cards

The following test bank account numbers can be used to test various Bank Verification scenarios.

Routing NumberAccount NumberStatus
02100002000000disabled
02100002100001enabled
02100002100002inconclusive