What is the difference between 'it' and 'test' in Jest? - javascript

I have two tests in my test group. One of the tests use it and the other one uses test. Both of them seem to be working very similarly. What is the difference between them?
describe('updateAll', () => {
it('no force', () => {
return updateAll(TableName, ["fileName"], {compandId: "test"})
.then(updatedItems => {
let undefinedCount = 0;
for (let item of updatedItems) {
undefinedCount += item === undefined ? 1 : 0;
}
// console.log("result", result);
expect(undefinedCount).toBe(updatedItems.length);
})
});
test('force update', () => {
return updateAll(TableName, ["fileName"], {compandId: "test"}, true)
.then(updatedItems => {
let undefinedCount = 0;
for (let item of updatedItems) {
undefinedCount += item === undefined ? 1 : 0;
}
// console.log("result", result);
expect(undefinedCount).toBe(0);
})
});
});
Update - Nov 2022:
It seems that test and it is interchangeable according to the official API of Jest. As #gwildu described here, you should choose one over the other for the sake of readability.

The Jest docs state it is an alias of test. So they are exactly the same from a functional point of view. They exist both to enable to make a readable English sentence from your test.

They do the same thing, but their names are different and with that their interaction with the name of the test.
test
What you write:
describe('yourModule', () => {
test('if it does this thing', () => {});
test('if it does the other thing', () => {});
});
What you get if something fails:
yourModule > if it does this thing
it
What you write:
describe('yourModule', () => {
it('should do this thing', () => {});
it('should do the other thing', () => {});
});
What you get if something fails:
yourModule > should do this thing
So it's about readability not about functionality.
In my opinion, it really has a point when it comes to read the result of a failing test that you haven't written yourself. It helps to faster understand what the test is about.
Some developer also shorten the Should do this thing to Does this thing which is a bit shorter and also fits semantically to the it notation.

As the other answers have clarified, they do the same thing.
I believe the two are offered to allow for either 1) "RSpec" style tests like:
const myBeverage = {
delicious: true,
sour: false,
};
describe('my beverage', () => {
it('is delicious', () => {
expect(myBeverage.delicious).toBeTruthy();
});
it('is not sour', () => {
expect(myBeverage.sour).toBeFalsy();
});
});
or 2) "xUnit" style tests like:
function sum(a, b) {
return a + b;
}
test('sum adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
Documentation:
https://jestjs.io/docs/en/api.html#describename-fn
https://jestjs.io/docs/en/api.html#testname-fn-timeout

As the jest documentation says, they are the same:
it alias
test(name, fn, timeout)
Also under the alias: it(name, fn, timeout)
And describe is just for when you prefer your tests to be organized into groups:
describe
describe(name, fn)
describe(name, fn) creates a block that groups together several related tests. For example, if you have a myBeverage object that is supposed to be delicious but not sour, you could test it with:
const myBeverage = {
delicious: true,
sour: false,
};
describe('my beverage', () => {
test('is delicious', () => {
expect(myBeverage.delicious).toBeTruthy();
});
test('is not sour', () => {
expect(myBeverage.sour).toBeFalsy();
});
});
This isn't required - you can write the test blocks directly at the top level. But this can be handy if you prefer your tests to be organized into groups.

You could replace it() with xit() to temporarily exclude a test from being executed; using it() and xit() is more eloquent than using test() and xit().
see Focusing and Excluding Tests

The following is an excerpt from the document:link
test(name, fn, timeout)
Also under the alias: it(name, fn, timeout)
All you need in a test file is the test method which runs a test. For
example, let's say there's a function inchesOfRain() that should be
zero. Your whole test could be: ......

const request = require('supertest');
const app = require('../app')
const {it, describe} = require('#jest/globals');
const { sequelize } = require('../models');
const {hash} = require('../helpers/bcrypt')
beforeAll(async ()=>{ await sequelize.queryInterface.bulkInsert('Customers', [{ username: "nikita", email:"nikita#mail.com", password: hash("nikita"), createdAt: new Date(), updatedAt: new Date() }]) })
afterAll(async ()=>{ await sequelize.queryInterface.bulkDelete('Customers', null, { truncate: true, cascade: true, restartIdentity: true }) })
describe('POST /customers/register', () => { it('should response with status 201', async ()=> { let customer = { username: "hello" , email:"hello#mail.com", password:"hello", } let response = await request(app) .post('/customers/register').send(customer) expect(response.status).toBe(201) // console.log(response.body, 'ini ressss') expect(response.body).toEqual({ message: "Input data Customer succeed", id: expect.any(Number), email: expect.any(String) }) });
it('should response with status 400', async()=>{ let customer = { username: "hello", password:"hello" } let response = await request(app) .post('/customers/register').send(customer) expect(response.status).toBe(400) // console.log(response.body,"<<<"); expect(response.body[0].message).toBe('Please input email') })

`const request = require('supertest');`
`const app = require('../app')`
`const {it, describe} = require('#jest/globals');`
`const { sequelize, Category, User, News , Customer, Bookmark} = require('../models');`
`const {hash} = require('../helpers/bcrypt');`
`const news = require('../models/news');`
`const {queryInterface} = sequelize`
`beforeAll(async()=>{
let userData = require("../data/user.json")
userData.forEach(el => {
el.password = hash(el.password)
el.createdAt = new Date()
el.updatedAt = new Date()
});`
`afterAll(async ()=>{
await Bookmark.destroy({
truncate: true,
cascade: true,
restartIdentity: true
})`
`describe('GET /customers/news', () => {
it("should response with status 200 1", async () => {
let response = await request(app)
.get('/customers/news')
// .set({access_token})
// console.log(response.body, "<<<<NEWS NIhH");
expect(response.status).toBe(200)
expect(response.body).toBeInstanceOf(Array)
})`
`it("should response with status 200 2", async()=>{
let response = await request(app)
.get('/customers/news?filter=1,2')
expect(response.status).toBe(200)
expect(response.body).toBeInstanceOf(Array)
})`

Jest haven't mentioned why they have two versions for the exact same functionality.
My guess is, it's only for convention. test is for unit tests, and it is for integration tests.

They are the same thing. I am using TypeScript as the programming language, and when I look into the definition file from the Jest package source code from /#types/jest/index.d.ts, I can see the following code.
Obviously, there are lots of different names of 'test', and you can use any of them.
declare var beforeAll: jest.Lifecycle;
declare var beforeEach: jest.Lifecycle;
declare var afterAll: jest.Lifecycle;
declare var afterEach: jest.Lifecycle;
declare var describe: jest.Describe;
declare var fdescribe: jest.Describe;
declare var xdescribe: jest.Describe;
declare var it: jest.It;
declare var fit: jest.It;
declare var xit: jest.It;
declare var test: jest.It;
declare var xtest: jest.It;

Related

Jest mocking a module with module pattern and having unique functionality on each test

I'm having trouble having a unique implementation of search from this module pattern setup I have (MyConnection.js) - here's similar to what I have:
// MyConnection.js
const MyConnection = () => {
const search = async (q) => {
//...some functionality
};
return {
search,
};
};
//JEST TESTS
const MyConnection = require('../MyConnection')
// this works - but it sets the search implementation
// for the whole test file I believe
jest.mock('../MyConnection', () => {
return jest.fn(()=>{
search: ()=> ['mocked', 'fn', 'results']
})
})
//the jest tests - I want different
// implementations of search in each test
describe('connection tests', ()=>{
it('test one', ()=>{
//Not sure if its something like this to set 'search' for each test? This doesn't work as is
MyConnection.search.mockImplementation((q)=>{`You searched ${q}`})
})
it('test two', ()=>{
MyConnection.search.mockImplementation((q)={q.length>32? 'a': 'b' }})
})
})
How can I get unique Jest mock implementations of that search function for each test?
You should make sure that mock MyConnection always returns the same connection object in your test file and the module you want to test.
MyConnection.js:
const MyConnection = () => {
const search = async (q) => {};
return {
search,
};
};
module.exports = MyConnection;
main.js:
const MyConnection = require('./MyConnection');
async function main(q) {
const conn = MyConnection();
return conn.search(q);
}
module.exports = main;
main.test.js:
const MyConnection = require('./MyConnection');
const main = require('./main');
jest.mock('./MyConnection', () => {
console.log('MyConnection module gets mocked');
const conn = { search: jest.fn() };
return jest.fn(() => conn);
});
const mConn = MyConnection();
describe('connection tests', () => {
it('test one', async () => {
mConn.search.mockImplementation((q) => {
return `You searched ${q}`;
});
const actual = await main('teresa teng');
expect(actual).toBe('You searched teresa teng');
});
it('test two', async () => {
mConn.search.mockImplementation((q) => {
return q.length > 32 ? 'a' : 'b';
});
const actual = await main('_');
expect(actual).toBe('b');
});
});
test result:
PASS examples/70132655/main.test.js (10.454 s)
connection tests
✓ test one (2 ms)
✓ test two (1 ms)
console.log
MyConnection module gets mocked
at examples/70132655/main.test.js:5:11
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 10.903 s
You can't mock it like this:
jest.mock('./MyConnection', () => {
console.log('MyConnection module gets mocked');
return jest.fn(() => { search: jest.fn() });
});
Why? Because every time you call the MyConnection() function in the test file or the file you want to test. It will return a new mock object({ search: jest.fn() }), the mock objects in file under test and test case are not same.

mock node config with jest

I introduce myself currently for the first time in jest and nodejs.
I am facing the problem that I have to mock two different values from the nodejs config.
jest.mock('config')
mockConfigTtl = require('config').get.mockReturnValue(100);
mockConfigScheduling = require('config').get.mockReturnValue('* * * * *');
the problem is that the second mockReturnValue overwrites the first one.
is there any possibility to separate booth mocks from each other?
Maybe with something like:
jest.mock('config')
mockConfigTtl = require('config').get('firstKey').mockReturnValue(100);
mockConfigScheduling = require('config').get('secondKey').mockReturnValue('* * * * *');
Since you would want to ensure your implementation would work with all possible configuration I consider it best to set multiple test scenarios in different describe block and in each of them use mockReturnValue and execute your implementation.
example:
const config = require('config');
jest.mock('config')
describe('my implementation', () => {
describe('with firstKey 100', () => {
let result
beforeAll(() => {
config.get.mockReturnValue(100)
result = myImplementation()
})
it('should result in ...', () => {
// your assertion here
})
})
describe('with firstKey different than 100', () => {
let result
beforeAll(() => {
config.get.mockReturnValue(1000)
result = myImplementation()
})
it('should result in ...', () => {
// your assertion here
})
})
})
or in case you want to test even more configuration you may use describe.each
const config = require('config');
jest.mock('config')
describe('my implementation', () => {
describe.each([
100,
200,
300
])('with firstKey: %d', (firstKey) => {
let result
beforeAll(() => {
config.get.mockReturnValue(firstKey)
result = myImplementation()
})
it('should match the snapshot', () => {
expect(result).toMatchSnapshot()
})
})
})
which would generate a snapshot with the result form your implementation and if it changes the test will fail unless snapshot is updated
You also can use config.get.mockImplementation(()=>'dummyData') to pass data for testing.
If in some cases you need original data you can use this snippet in your tests:
config.get.mockImplementation((key) => {
const originalModule = jest.requireActual('config')
return originalModule.get(key)
})

Best Way to Test Promises in Jest

Unless I'm misunderstanding something, the resolves and rejects (https://facebook.github.io/jest/docs/expect.html#resolves) won't be available until vNext. What is the recommended way now/in the meantime to test promises with Jest? Is it just putting expects in the thens and catches?
For example:
describe('Fetching', () => {
const filters = {
startDate: '2015-09-01'
};
const api = new TestApiTransport();
it('should reject if no startdate is given', () => {
MyService.fetch().catch(e => expect(e).toBeTruthy()); // see rejects/resolves in v20+
});
it('should return expected data', () => {
MyService.fetch(filters, null, api).then(serviceObjects => {
expect(serviceObjects).toHaveLength(2);
}).catch(e => console.log(e));
});
});
UPDATE 15 June 2019: Not too long after I posted this question, Jest started supporting this out of the box. I changed the accepted answer below to reflect the currently best way to do this.
UPDATE 8 Dec 2021: At some point Jest started supporting async/await. So while other methods noted work, I've taken to simply (for most cases) using something like:
it('should do something', async () => {
const expected = true;
expect(await funcToTest()).toEqual(expected);
});
As with most cases, async/await is much more readable than alternatives. The only case I use resolves or rejects now is for simple cases like:
it('should not throw when doing something', async () => {
await expect(funcToTest()).resolves.not.toThrow();
});
it('should throw when something is wrong', async () => {
await expect(funcToTest()).rejects.toThrow();
});
Nowadays you can write it in this way as well: docs
describe('Fetching', () => {
const filters = {
startDate: '2015-09-01'
};
const api = new TestApiTransport();
it('should reject if no startdate is given', () => {
expect.assertions(1);
return expect(MyService.fetch()).rejects.toEqual({
error: 'Your code message',
});
});
it('should return expected data', () => {
expect.assertions(1);
return expect(MyService.fetch(filters, null, api)).resolves.toEqual(extectedObjectFromApi);
});
});
Update (06.01.2019)
Agree that the accepted answer doesn't work correctly as line
expect.assertions(1); does all the magic. Link to docs
expect.assertions(number) verifies that a certain number of assertions
are called during a test. This is often useful when testing
asynchronous code, in order to make sure that assertions in a callback
actually got called.
So putting this line at the top will control that the specific number of assertions are made by the time when the test is run.
Either return a promise and expect in the resolve or catch
describe('Fetching', () = > {
const filters = {
startDate: '2015-09-01'
};
const api = new TestApiTransport();
it('should reject if no startdate is given', () = > {
return MyService.fetch()
.catch (e => expect(e).toBeTruthy()); // see rejects/resolves in v20+
});
it('should return expected data', () = > {
return MyService.fetch(filters, null, api)
.then(serviceObjects => {
expect(serviceObjects).toHaveLength(2);
})
});
});
or using async/await
describe('Fetching', () = > {
const filters = {
startDate: '2015-09-01'
};
const api = new TestApiTransport();
it('should reject if no startdate is given', async() = > {
try {
const r = await MyService.fetch()
} catch (e) {
expect(e).toBeTruthy()
}
});
it('should return expected data', async() = > {
const serviceObjects = await MyService.fetch(filters, null, api)
expect(serviceObjects).toHaveLength(2);
});
});
I was able to test JEST with AXIOS for HTTP REST calls like this.
it('has an API worth testing', async () => {
let httpResult = null;
await callThefunctionThatReturnsPromiseToMakeTheAxiosApiCall()
.then(function(result) {httpResult=result;})
.catch(function(err) {httpResult=err;});
expect(httpResult.data.myData).toBe("myExpectedValue");
});
or
it('has an API worth testing', async () => {
let httpResult = await callThefunctionThatReturnsPromiseToMakeTheAxiosApiCall();
expect(httpResult.data.myData).toBe("myExpectedValue");
});
For additional Jest matchers maintained by the Jest Community check out jest-extended.
https://jestjs.io/docs/expect
Using jest-extended you can expect your promise toResolve() or toReject(). Then you can expect the result or the error to match something. For example:
test('OK status', async () => {
const request = fetch(...)
await expect(request).toResolve() // First, make sure it resolves
const data = await request
expect(data).toEqual(...) // Then test the result
})
test('ERROR status', async () => {
const request = fetch(...)
await expect(request).toReject() // First, make sure it rejects
await request.catch((error) => expect(error).toBe('...')) // Then test the error
})

Using Sinon to mock a constant/variable?

I'm fairly new to testing and even newer to Sinon.
Here I have an express route set up:
import context = require("aws-lambda-mock-context");
this.router.post('/', this.entryPoint);
public entryPoint(req: Request, res: Response, next: NextFunction) {
const ctx = context();
alexaService.execute(req.body, ctx);
ctx.Promise
.then((resp: Response) => res.status(200).json(resp))
.catch((err: Error) => res.status(500));
}
My aim is to test that the post call to / runs appropriately. My test script is:
describe('/POST /', () => {
it('should post', () => {
chai.request(app)
.post('/v2')
.end((err, res) => {
expect(res).to.be.ok;
});
});
});
Though my test passes it returns status: 500 due to the const ctx = context() not being recognized. Is there an appropriate/correct way to spy on the variable ctx and return a mock variable within my test using Sinon? I've been spinning my wheels here for so long.
This is a common problem which I've come accross myself. I've tested multiple solutions, the one I've found to work best is Mockery.
It works like this: before you require your module under test, you tell Mockery to substitute modules the module under test requires with mocks.
For your code, it would look something like this:
const mockery = require('mockery');
const { spy } = require('sinon');
describe('/POST /', () => {
let ctxSpy;
beforeEach(() => {
mockery.enable({
useCleanCache: true,
warnOnUnregistered: false
});
ctxSpy = spy();
mockery.registerMock('"aws-lambda-mock-context"', ctxSpy);
// change this to require the module under test
const myRouterModule = require('my-router-module');
myRouterModule.entryPoint({}, {}, () => {});
return ctxSpy;
});
it('should call ctx', () => {
expect(ctxSpy).called.to.be.ok;
});
afterEach(() => {
mockery.deregisterAll();
mockery.disable();
});
});

how do i test my async jasmine/nodejs/promise code using Spies

I have a module (example has been simplified) called process-promise which has a single function that takes a Promise as input and processes it - it also calls other functions using modules outside it as follows:
//<process-promise.js>
let User = require('user-module');
let processPromise = (promiseObj) => {
let user = new User();
promiseObj.then((full_name) => {
const [ fname, sname ] = full_name.split(' ');
if (fname && sname) {
user.setDetails(fname, sname);
} else{
console.log('nothing happened');
}
}).catch((err) => {
console.log(err.message);
});
};
module.exports = {
processPromise
};
I am trying to unit test the above function using Jasmine, Rewire and Jasmine spies as per following code
let rewire = require('rewire');
let mod = rewire('process-promise');
describe('process-promise module', () => {
beforeEach(() => {
this.fakeUser = createSpyObj('fake-user', ['setDetails']);
this.fakeUserMod = jasmine.createSpy('fake-user-mod');
this.fakeUserMod.and.returnValue(this.fakeUser)
this.revert = mod.__set__({
User: this.fakeUserMod
});
});
afterEach(() => {
this.revert();
});
it('fakeUser.setDetails should be called', (done) => {
mod.processPromise(Promise.resolve('user name'));
done();
expect(this.fakeUser.setDetails).toHaveBeenCalledWith('user','name');
});
});
I expect that the Spy this.fakeUser.setDetails should get called but i get the message from Jasmine "Expected spy fake-user.setAll to have been called with [ 'user', 'name' ] but it was never called." - the problem seems to be the fact the promise is Async but i've included the done function as other SO questions have suggested but this doesn't seem to resolve the problem for me. What's the issue with my code? most other SO questions relate to angular so don't help with my problem.
You are on the right track, the promise is asynchronous and then done function in your test is called before the promise resolved to a value. The done function is used as a callback to tell the test engine, that all your asynchronous code has completed. It should be called after the promise resolved to a value (or failed for that matter).
In order to do that, you'd need to make the following adjustments to your code:
//<process-promise.js>
let User = require('user-module');
let processPromise = (promiseObj) => {
let user = new User();
// return a promise, to allow a client to chain a .then call
return promiseObj.then((full_name) => {
const [ fname, sname ] = full_name.split(' ');
if (fname && sname) {
user.setDetails(fname, sname);
} else{
console.log('nothing happened');
}
}).catch((err) => {
console.log(err.message);
});
};
module.exports = {
processPromise
};
The test would then look like this:
it('fakeUser.setAll should be called', (done) => {
mod.processPromise(Promise.resolve('user name')).then(() => {
expect(this.fakeUser.setAll).toHaveBeenCalledWith('user','name');
done();
}).catch(done);
});
Be sure to add .catch(done). This will make sure your test fails in case the promise resolves to an error.
Is probable that, by the time your test code execute, the promise has not propagated to the code under test. And simply calling done() doesn't the synchronization magic.
I'm not familiar with rewire so I will share an example using
proxyquire
const proxyquire = require('proxyquire');
describe('process-promise module', () => {
const fakeUser = { setDetails: jasmine.createSpy('setDetails') };
const fakeUserMod = jasmine.createSpy('fake-user-mod').and.returnValue(fakeUser);
const promiseObj = Promise.resolve('user name');
beforeEach((done) => {
const processPromiseMod = proxyquire('process-promise', {
'user-module': fakeUserMod,
});
processPromiseMod.processPromise(promiseObj);
promiseObj.then(() => done());
});
it('fakeUser.setDetails should be called', () => {
expect(fakeUser.setDetails).toHaveBeenCalledWith('user','name');
});
});
Also note that setAll doesn't exist in the fakeUser instance. I guess you mean setDetails instead of setAll.

Categories