Jest - is it possible to test specific variables within an async module? - javascript

I am wondering if it is possible to test variables for specific values within a module call. For example:
module.exports = async(request, context, postgres) => {
let userId = request.user.id;
let balanceQuery = 'SELECT balance FROM account WHERE user_id = $1';
let userBalanceStr = await postgres.query(balanceQuery,[userId]).then(result => result.rows[0].balance);
let totalCount = BigFunction(userBalanceStr);
......
}
How would I go about testing/accessing the value of balanceQuery or userBalanceStr within a jest test?
I'm only able to access the userId portion of the query, and I am sort of confused how to go about accessing other variables within the module.

You can achieve that by mocking postgres.query and BigFunction and then asserting the arguments passed in each call. And to be able to test the async code, you can implement an async test.
API reference: Mock Functions
When mocking postgres.query, you could implement a fake returning value, which will be assigned to userBalanceStr, and then can be asserted with the mocked BigFunction.
To mock BigFunction, you can either mock its importation present in the module under test, or changing the exported function to pass BigFunction as a dependency, like postgres, passing the mock function directly as argument.

Related

How should I get API token before executing Unit Test cases?

Within my unit test cases, I'm trying to do unit tests against some data in the API therefore an API token is required. I'm hoping to find a way to call token API and store it in Redux before firing any API.
I'm aware of setup.js in Jest, tried calling my API there and store in Redux didn't work well. I don't think the setup.js waited the method to finish completely before starting the unit test.
// Within the Setup.js, I was calling method directly
const getAPItoken = async() => {
await getToken();
}
getAPItoken();
Currently I'm getting the API token in 1 of the Unit Test files. Upon the method completion, rest of the Unit Tests will run fine since they are getting the API token from Redux.
Sample of what I'm doing now
describe('Get API token', () => {
test('it should return true after getting token', async () => {
// Within the method itself, it actually store the token to redux upon receiving from API, also it will return TRUE upon success
const retrievedToken = await getToken();
expect(retrievedToken).toBeTruthy();
});
Is there a better way to handle this?
You can use globalSetup. It accepts an async function that is triggered once before all test suites.
So you can optain the API key and set it on node global object so you can access if from anywhere.
// setup.js
module.exports = async () => {
global.__API_KEY__ = 'yoru API key';
};
// jest.config.js
module.exports = {
globalSetup: './setup.js',
};

How to properly create unit test for logic inside arrow function

I'm working on an existing NodeJS web service using HapiJS, Hapi Lab for testing along with Sinon. The service connects to a Postgres DB using massiveJs. There's a method implemented by someone else, that doesn't have unit tests. Now I'm reusing this method and I want to implement some unit tests for it.
This method executes a massivejs transaction inside of it, persisting to several tables.
async createSomething(payload) {
const { code, member } = payload;
const foundCompany = await this.rawDb.ethnics.tenants.findOne({ code });
if (foundCompany && foundCompany.companyId) {
const { companyId } = foundCompany;
const { foreignId } = member;
return await this.rawDb.withTransaction(async (tx) => {
const foundMember = await tx.ethnics.members.findOne({ foreign_id: foreignId, company_id: companyId });
if (!foundMember) {
//some business logic...
const newMember = await tx.ethnics.members.insert(member);
//more business logic persisting to other tables...
return newMember;
}
});
}
}
Problem is, I don't know how to stub stuff only inside the arrow function, without stubbing the entire arrow function. I just want to stub the calls of tx. I also don't want to use a database but stub the rawDb property. Is that doable from unit testing perspective?
Yes it is doable. There are 2 alternatives:
Stub MassiveJS methods directly.
Example to stub massive method findOne:
const massive = require('massive');
const sinon = require('sinon');
// Stub massive.Readable class method findOne.
// You need to find where is the real method findOne and stub it.
const stub = sinon.stub(massive.Readable, 'findOne');
// You can resolve it.
stub.resolves();
// Or you can throw it.
stub.throws(new Error('xxx'));
Use pg in memory for test.
Just for testing purpose, you can use module like: test-pg-pool or pg-mem. Before testing, start the test pg and after the test finish, destroy it.

Is it possible to test a non https function with firebase?

I have a local function that is called like so:
exports.testFunction = functions.pubsub
.schedule(schedule)
.onRun(() => test.scraper('123'))
However, test.scraper() is not an HTTPS function, it's just a regular function.
How can I test it using firebase without having to wrap it in a callable https function? Do I have to use the function shell?
Thanks.
As Doug said, you can test this separately if that's what you need. However, according to the official documentation, you can also use offline unit tests if it suits your workflow. You still have to wrap some functions, but it satisfies testing.
Example from the article with stubbed data and Sinon:
// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onCreate((snapshot, context) => {
// Grab the current value of what was written to the Realtime Database.
const original = snapshot.val();
console.log('Uppercasing', context.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the Realtime Database returns a Promise.
return snapshot.ref.parent.child('uppercase').set(uppercase);
});

use jest mock to run async function in isolation

async await jest mock http request in node.jest
I can write my test like so
it('works with async/await', async () => {
expect.assertions(1);
const data = await user.getUserName(4);
expect(data).toEqual('Mark');
});
but getUserName will be executed and it hit my backend and database, how to mock the function so that my test can be run in isolation?
You need to actually mock the user object. I don't have full context but you probably have something like const User = require('../user.js'). So with jest you can mock this by doing
jest.mock('../user.js')
jest.mock accepts also a callback where you can add there your own implementation but I prefer having a seperate mocks folder where you can keep your own implementation of the mock as it might make the tests a bit more cluttered.
You can check how it's used here where you call jest.mock and then the mock implementation.

Aliases for jest.fn()?

I have two different libraries that I'm using to make mocks in Jest. The libraries have the same function called get. This is a problem for my current implementation since get is used by two different libraries is it possible to use an alias for mock functions (jest.fn()) or maybe some kind of workaround that doesn't ruin the integrity of the current implementation?
Here is my current implementation and I would I like to keep this way if possible:
let get: jest.Mock<{}>
jest.mock('rxjs/ajax', () => {
get = jest.fn()
return { ajax: { get } }
})
let get as cookieGet: jest.Mock<()> // Can I do something like this
jest.mock('js-cookie', () => {
get = jest.fn()
return { get }
})
I'm not too familiar with aliases in JS or they Jest handles things like this so any help is much appreciated.
It's unnecessary to use { get } shorthand property syntax for object literal if it results in name collisions.
Another problem is that a variable needs to have mock prefix in order to be used in the scope of jest.mock factory function. As the documentation states,
A limitation with the factory parameter is that, since calls to jest.mock() are hoisted to the top of the file, it's not possible to first define a variable and then use it in the factory. An exception is made for variables that start with the word 'mock'. It's up to you to guarantee that they will be initialized on time!
It can be:
import ... from 'rxjs/ajax';
import ... from 'js-cookie';
let mockRxAjaxGet: jest.Mock<{}>
jest.mock('rxjs/ajax', () => {
mockRxAjaxGet = jest.fn()
return { ajax: { get: mockRxAjaxGet } }
})
let mockJsCookieGet: jest.Mock<()>
jest.mock('js-cookie', () => {
mockJsCookieGet = jest.fn()
return { get: mockJsCookieGet }
})
The problem is that once jest.mock is hoisted above imports, it will be evaluated when let variables are in temporal dead zone and cannot be assigned.
So let should be preferably changed to var, which is hoisted. Or mocked function be imported as usual and used with get as jest.Mock<...> where a spy is expected. mocked helper can be used to enforce TypeScript type safety.

Categories