Skip to main content
Version: Next

Unit Testing Custom Actions

Unit tests help prevent regressions in custom action functionality. The createTemplateAction function that is the core of a custom action can be difficult to mock. There are helper methods that can assist.

Mocking the Context​

The handler property of the createTemplateAction input object expects a context. You can create a mock context using the code below:

import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils';

const mockContext = createMockActionContext({
input: { repoUrl: 'dev.azure.com?repo=repo&owner=owner&organization=org' },
});

await action.handler(mockContext);

expect(mockContext.output).toHaveBeenCalledWith(
'remoteUrl',
'https://dev.azure.com/organization/project/_git/repo',
);

Mocking a Workspace within the Context object​

One thing to be aware about: if you would like to call createMockActionContext inside it, you have to provide a workspacePath. By default, createMockActionContext uses import { createMockDirectory } from '@backstage/backend-test-utils'; to create it for you. You can use the code below to customize the workspacePath without using the default workspace of the createMockActionContext function.

describe('github:autolinks:create', async () => {
const workspacePath = createMockDirectory().resolve('workspace');
// ...

it('should call the githubApis for creating alphanumeric autolink reference', async () => {
// ...
await action.handler(
createMockActionContext({
input: {
repoUrl: 'github.com?repo=repo&owner=owner',
keyPrefix: 'TICKET-',
urlTemplate: 'https://example.com/TICKET?query=<num>',
},
workspacePath,
}),
);
//...
});
});

Mocking a Config Core Service​

If your custom Action requires the Config Core Service within execution of the handler(ctx) such as the custom action below, mocking the context object can be done by building a mockContext with the ConfigReader function within the @backstage/config package.

// custom-action.ts
import { Config } from '@backstage/config';

export const customActionRequiringConfigCoreService = (config: Config) => {
const fieldRequiringValueFromConfig = config.getString('app.service.url');
return createTemplateAction({
...
async handler(ctx) {
// Some code requiring the config const
ctx.logger.info(fieldRequiringValueFromConfig);
}
})
}
// custom-action.test.ts
import { ConfigReader } from '@backstage/config';
import { customActionRequiringConfigCoreService } from './custom-action.ts';
...
const mockConfig = new ConfigReader({
app: {
service: {
url: 'https://api.service.io/graphql',
apiKeyId: '123',
apiKeySecret: '123abc',
},
},
});
...
const action = customActionRequiringConfigCoreService(mockConfig);
await action.handler({
...mockContext
})

Mocking a Cache Core Service​

Similar to the Mocking a Config Core Service section above, if your custom action expects a Cache Core Service Object as part of the function input, you can mock it out with the following:

import { CacheService } from '@backstage/backend-plugin-api';

const mockCacheServiceMethods = {
get: jest.fn(),
set: jest.fn(),
delete: jest.fn(),
};

const mockCacheService = mockCacheServiceMethods as unknown as CacheService;

const action = customActionRequiringCacheCoreService(mockCacheService);
...