Can you test CDK constructs with Mocha instead of Jest? - jestjs

The developer guide makes it sound like you can only use jest with the cdk. However, our projects currently use mocha. We could use jest for the cdk and keep everything else the same, but we're wondering if anyone has had any luck using mocha to test the cdk.
EDIT:
So far, it seems to have worked with this simple set up:
installed ts-node, mocha
ran with mocha -r ts-node/register file.test.ts
file.test.ts:
import { expect as expectCDK, matchTemplate, MatchStyle } from '#aws-cdk/assert';
import * as cdk from '#aws-cdk/core';
import * as Infrastructure from '../lib/infrastructure-stack';
it('Empty Stack', () => {
const app = new cdk.App();
// WHEN
const stack = new Infrastructure.InfrastructureStack(app, 'MyTestStack');
// THEN
expectCDK(stack).to(matchTemplate({
"Resources": {}
}, MatchStyle.EXACT))
});
Has anyone tried more complicated tests?

I believe the cdk assert library was written with jest in mind but I couldn't find any classes that will only work with jest so it should work with any test framework since they provide their own expectations function, cdkExpect

Related

Using environment variables in Node and Jest

I have a NodeJS app. For a long time I've been using Mocha to run tests. I simply had a package.json like
"dev": "node index --env=dev",
"start": "node index",
"test": "mocha './test/**/*.spec' --env=dev"
Now I am switching to Jest for a variety of reasons, and the test script becomes simply
"test":"jest"
Within my test suites, I spin up an instance of my app. In functions throughout my app there are decisions based on the environment. Simplified example:
const ProcessArgs = require("minimist")(process.argv);
export function uploadImage(image){
if(ProcessArgs.env === "dev"){
return writeToLocalTestFolder(image);
}
else {
return uploadToCloud(image);
}
}
So this used to look at the --env=dev arg, but that's no longer there. (If I try to add --env=dev to Jest, well that's an entirely different issue). I do in fact properly use Heroku's secrets for my actual important env variables like API keys, etc, but I now seem to be stuck here on how I should tell Jest what env it is in, since it needs env=node to work properly.
Mocking out environment variables in Jest is a little bit more tricky as there really is no way to mock them out before the code that you want to import gets evaluated. However, there is a handy workaround. You can mock out the library that is contains the environment variables and have it return specifically what you need.
Referring to your example, you can mock out what environment variables are returned by minimist by using the following code (make sure it is used before the describe of your test suite:
jest.mock('minimist', () => {
return () => ({ env: 'dev'});
});
The if(ProcessArgs.env === "dev"){ conditional check should run as expected.

Jest Mock Globally From a Node Module

I am writing a series of Node modules that require a bunch of common Jest mocks to be setup before I can write unit tests.
I am trying to refactor the unit test setup into a separate module so I can avoid rewriting the setup each time.
The problem is that when I import the following code as a module it no longer mocks the other libraries, while it works just fine when the mocks are set up in a utility file.
Working Code:
jest.mock('#actions/core')
jest.mock('#actions/github')
const { GitHub, context} = require('#actions/github')
const core = require('#actions/core')
GitHub.mockImplementation(() => {
return {
{
repos: {
getContents: jest.fn()
}
}
}
}
module.exports = { core, GitHub, context }
I keep this in a utils.js file next to my test files and import it like const { core, GitHub, context } = require('./utils.js') and everything mocks as I expect. I can run expect().toHaveBeenCalledTimes() and get the numbers I expect.
The problem appears when I move utils.js to another module and require it.
I know that at bottom of the jest documentation it says "...Another file that imports the module will get the original implementation even if it runs after the test file that mocks the module." But I am seeing this work inconsistently with the external file.
Anyone know how to get mocking setup in external modules?

How to enable Intl in node for tests using Jest?

I am trying to test currency formatting functions and while they work in the browser, they are not working in my test environment. I understand that intl does not come standard with node, so I have added intl and full-icu to my devDependencies, but this has not helped
var IntlPolyfill = require('intl');
require('full-icu');
Intl.NumberFormat = IntlPolyfill.NumberFormat;
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;
expect((4000).toLocaleString('fr-ca',{style: 'currency',currency: 'CAD',currencyDisplay: 'symbol'})).toBe('4 000,00 $')
/* test output:
Expected: "4 000,00 $"
Received: "CA$ 4,000.00"
*/
In IE11, Chrome and Firefox I get the expected result ("14 337,00 $") but in Node I am not.
I have found little help online, but did find reference to the 2 libraries I am using here. What do I need to do for the node environment used by Jest to have access to the proper Intl functions. The function is there, but seems to be returning the US formatting for every locale.
First comes my answer that immediately came into my mind and solves the problem, but it in fact is an anti-pattern - I simply leave it here so others can learn from it. The good solution follows further down in my answer:
You simply can import the node module intl into you Jest test and pass it to the JS you are testing:
import Intl from 'intl';
import CurrencyFormatter from '../../../js/modules/CurrencyFormatter.js';
describe('CurrencyFormatter', () => {
let formatter = null;
beforeEach(() => {
formatter = new CurrencyFormatter(Intl);
});
test([...]);
});
And in your JS you conditionally use the intl module if passed by Jest:
export default class CurrencyFormatter {
constructor(optionalIntl) {
// Needed for Jest, because it doesn't support Intl natively:
if (optionalIntl != null) {
global.Intl = optionalIntl;
}
[...]
}
[...]
}
This way you keep the superfluous intl module out of your production code and make it available to Jest for tests only. But as you can clearly see, this means adjusting the production code for the sole purpose of satisfying the test's needs. This is called an anti-pattern. The production code should always serve production needs (and readability/understandability for other devs, ofc), not the testing environment.
So for that reason you should rather follow the second solution, which adidtionally has the advantage of fixing the issue for all similar tests in the suite and of using much less code changes. In fact it does not change the code but only the configuration:
[Edit:]
As a colleague mentioned, this indeed is an anti-pattern - do not adapt your production code in order to pass the test. Yet, there is a better option, using the node module full-icu by passing it to node in the scripts entry for jest, i.e. do something like this in your package.json:
[...]
"scripts": {
"test": "node --icu-data-dir=node_modules/full-icu node_modules/jest/bin/jest.js --version --config=./jest.config.js"
},
[...]

Canvas Issues when Unit Testing Phaser and TypeScript with Mocha on Node

I'm currently developing a TypeScript/Phaser (2.6.2) project and attempting to run unit tests, set up to use Mocha and Chai (with ts-node/register and jsdom-global/register) on a Node environment with node-canvas installed using the installation instructions for Windows.
The project builds using webpack and runs in the browser correctly, as does Compodoc, and tests that do not require Phaser are also able to execute and pass. Example class/test:
phaser.game.class.ts:
import { Phaser } from 'phaser'
export class Game extends Phaser.Game {
constructor() { super(1280, 720, Phaser.AUTO, 'game') }
}
phaser.game.class.spec.ts:
import 'mocha'
import { expect } from 'chai'
import { Game } from './phaser.game.class'
describe('Game', () => {
it('should be defined', () => {
expect(Game).to.exist
})
})
However when running tests that do import Phaser I receive an error:
context.drawImage(magenta, 0, 0);
^
TypeError: Expected object, number and number
Looking at this issue I believe it may be related to Phaser using a different canvas build to that which is installed with node-canvas, but I cannot force the use of a certain node-canvas version (as is described there) as ultimately this will be run in a browser - is this the case, and is there any method to ensure the canvas version just for the tests?
Alternatively I've looked into using phaser-mock and proxyquire (which I would prefer for simply testing isolated class logic), but am experiencing other issues and am unable to find an example implementation combining the two.
Any information would be much appreciated!

Changing mocha tests to jest tests

I'm having trouble changing my mocha tests to jest tests.
I have three test files with three classes: FirstTestGroup, SecondTestGroup and ThirdTestGroup, each with a static execute method, that contains some tests, like this:
class FirstTestGroup {
execute(params) {
describe('some tests', function tests() {
it('one test', () => {
// uses params
});
...
});
...
}
}
Each of those execute methods use the same parameters. These parameters are created in an async before call, like shown bellow.
describe('My Tests', function testSuite() {
let params;
before('param creation', async function asyncFunc() {
// creates params asynchronously
});
it('should pass all', () => {
FirstTestGroup.execute(params);
SecondTestGroup.execute(params);
ThirdTestGroup.execute(params);
});
});
The it('should pass all', ...) is needed because everything inside a describe is run instantly, so params would be passed as null without it. This works in mocha because "it"s can be nested, but apparently this is not the case for jest.
I could make the beforeAll (equivalent of before in jest) be called each time before a test group is run, but I didn't want to do that as this seems inefficient.
I could also place the code inside the before call before the describe('My Tests', ...) is defined. This seems wrong as this should be part of the describe only.
I couldn't find anything in the jest documentation that could help me with that.
Am I doing something wrong? Is there a way to achieve this using jest, even if I have to restructure the tests? But I'd like to keep the tests in different files.
This is my first question here, so please tell me if more info is needed as I'm not used writting here.
Have you tried Jest-Codemods yet?
Jest-Codemods allows you to convert your Mocha, AVA, Jasmine tests into equivalent Jest tests. It would also help you migrate assertion libraries such as Chai and Expect.
The way you do it:
Install jest-codemods with npm install -g jest-codemods
Go to your project and execute jest-codemods
It will ask you Which test library would you like to migrate from?
Select Mocha by using arrow keys (As you want to migrate from Mocha)
Next, It will ask you Will you be using Jest on Node.js as your test runner?
Select Yes, use the globals provided by Jest (recommended)
Last, You need to provide file name on which you are working to migrate from Mocha to Jest.
And you are done!
It will automatically migrate your code from Mocha to Jest. No need to touch code. That's one of the most powerful feature of Jest which would save your time and you don't need to worry about changing mocha tests to jest tests.

Resources