Cannot read property of undefined thrown from running NPM and PACT - javascript

I'm trying to follow the PACT workshop example with some alternate data.
This may be more of a Javascript/Node question but I'm a but stumped, as a novice.
Given a consumer.spec.js file of:
const chai = require('chai');
const nock = require('nock');
const chaiAsPromised = require('chai-as-promised');
const expect = chai.expect;
const API_PORT = process.env.API_PORT || 9123;
chai.use(chaiAsPromised);
const API_HOST = `http://localhost:${API_PORT}`;
describe('Consumer', () => {
describe('when a call to the Provider is made', () => {
const clothingStatus = 'hello';
const {emailClothingOfferStatus} = require('../client');
it('can process the HTML payload from the provider', () => {
nock(API_HOST)
.get('/provider')
.query({validPermStatus:'hello'})
.reply(200, {
test:'NO',
validPermStatus: clothingStatus,
count: 1000,
});
const response = emailClothingOfferStatus(clothingStatus);
return expect(response.body.clothingStatus).to.eventually.equal('hello')
})
})
});
and a client .js file of:
const request = require('superagent');
const API_HOST = process.env.API_HOST || 'http://localhost';
const API_PORT = process.env.API_PORT || 9123;
const API_ENDPOINT = `${API_HOST}:${API_PORT}`;
// Fetch provider data
const emailClothingOfferStatus = emailPermChoice => {
let withEmailClothing = {};
const emailClothingGrantedRegex = 'hello';
if(emailPermChoice){
console.log(emailPermChoice);
withEmailClothing = {validPermStatus: emailPermChoice}
}
return request
.get(`${API_ENDPOINT}/provider`)
.query(withEmailClothing)
.then(
res => {
if (res.body.validPermStatus.match(emailClothingGrantedRegex)) {
return {
clothingStatus: (res.body.validPermStatus),
}
} else {
throw new Error('Could not verify email clothing offer status')
}
},
err => {
throw new Error(`Error from response: ${err.body}`)
}
)
};
module.exports = {
emailClothingOfferStatus,
};
and I have the following in my package.json scripts:
"test:consumer": "./node_modules/.bin/mocha --timeout 150000 pact/consumer/test/consumer.spec.js",
When I run npm run test:consumer, I get:
1) Consumer
when a call to the Provider is made
can process the HTML payload from the provider:
TypeError: Cannot read property 'clothingStatus' of undefined
at Context.it (pact/consumer/test/consumer.spec.js:29:35)
I'm sure it's something obvious but can anyone help?

Two things stand out to me as a problem:
The test above is a normal unit test designed to show how unit tests won't catch contract issues, and leads you into why Pact is useful (In case this wasn't clear). In short, it's not a Pact test at all - I can tell because it's using Nock, meaning the expected requests will never reach Pact. I can also tell because the Pact package doesn't appear to be imported. You want to model from this file https://github.com/DiUS/pact-workshop-js/blob/master/consumer/test/consumerPact.spec.js
The response value is a Promise, which means you can't do return expect(response.body.clothingStatus).to.eventually.equal('hello') because response is a promise, so body will be undefined and clothingStatus is not a property of that. The chai eventually API is useful for this sort of test, but as I understand, it has to work directly with a Promise - you could do expect(response).to... and then chai can go to work.

Your function emailClothingOfferStatus returns response.then() which is a promise and not an actual response.
Therefore response.body is undefined.
You should be able to test the result like this:
const response = emailClothingOfferStatus(clothingStatus);
response.then((res) => {
expect(res.body.clothingStatus).to.eventually.equal('hello')
})

Related

How to mock fs module together with unionfs?

I have written a test case that successfully load files into virtual FS, and at the same time mounted a virtual volume as below
describe("should work", () => {
const { vol } = require("memfs");
afterEach(() => vol.reset());
beforeEach(() => {
vol.mkdirSync(process.cwd(), { recursive: true });
jest.resetModules();
jest.resetAllMocks();
});
it("should be able to mock fs that being called in actual code", async () => {
jest.mock("fs", () => {
return ufs //
.use(jest.requireActual("fs"))
.use(createFsFromVolume(vol) as any);
});
jest.mock("fs/promises", () => {
return ufs //
.use(jest.requireActual("fs/promises"))
.use(createFsFromVolume(vol) as any);
});
const { createFsFromVolume } = require("memfs");
const { ufs } = require("unionfs");
const { countFile } = require("../src/ops/fs");
vol.fromJSON(
{
"./some/README.md": "1",
"./some/index.js": "2",
"./destination": null,
},
"/app"
);
const result = ufs.readdirSync(process.cwd());
const result2 = ufs.readdirSync("/app");
const result3 = await countFile("/app");
console.log({ result, result2, result3 });
});
});
By using ufs.readdirSync, I can access to virtual FS and indeed result giving me files that loaded from disc into virtual FS, result2 representing /app which is a new volume created from vol.fromJSON.
Now my problem is I am unable to get the result for result3, which is calling countFile method as below
import fsPromises from "fs/promises";
export const countFile = async (path: string) => {
const result = await fsPromises.readdir(path);
return result.length;
};
I'm getting error
Error: ENOENT: no such file or directory, scandir '/app'
which I think it's because countFile is accessing the actual FS instead of the virtual despite I've had jest.mock('fs/promises')?
Please if anyone can provide some lead?
This is the function you want to unit test.
//CommonJS version
const fsPromises = require('fs/promises');
const countFile = async (path) => {
const result = await fsPromises.readdir(path);
return result.length;
};
module.exports = {
countFile
}
Now, how you would normally go about this, is to mock fsPromises. In this example specifically readdir() since that is the function being used in countFile.
This is what we call: a stub.
A skeletal or special-purpose implementation of a software component, used to develop or test a component that calls or is otherwise dependent on it. It replaces a called component.
const {countFile} = require('./index');
const {readdir} = require("fs/promises");
jest.mock('fs/promises');
beforeEach(() => {
readdir.mockReset();
});
it("When testing countFile, given string, then return files", async () => {
const path = "/path/to/dir";
// vvvvvvv STUB HERE
readdir.mockResolvedValueOnce(["src", "node_modules", "package-lock.json" ,"package.json"]);
const res = await countFile(path);
expect(res).toBe(4);
})
You do this because you're unit testing. You don't want to be dependent on other functions because that fails to be a unit test and more integration test. Secondly, it's a third-party library, which is maintained/tested by someone else.
Here is where your scenario applies. From my perspective, your objective isn't to test countFile() rather, to test fsPromises and maybe test functionality to read virtual file-systems: unionfs. If so then, fsPromises doesn't need to really be mocked.

Jest mockedCoeus.mockImplementation throws a TypeError

I'm using Jest to write a test and mock a function that calls an HTTP request.
import { mocked } from "ts-jest/utils";
import * as pull from "../src/pull";
import fs = require("fs");
// read the reponse data from a file.
const response = JSON.parse(
fs.readFileSync("./__fixtures__/pr.json", "utf8")
);
// have jest mock the function and set it's response.
jest.mock("../src/pull");
const mockedCoeus = mocked(pull.getPullRequest, true);
mockedCoeus.mockImplementation(async () => {
return response as any;
});
// write the test.
describe("#get details for a PR", () => {
it("should load user data", async () => {
const data = await pull.getPullRequest(165, "data-ios");
expect(data).toBeDefined();
expect(data.updated_at).toEqual("2020-04-10T16:46:30Z");
});
});
The test passes, however, I get the following error when running npm jest
TypeError: mockedCoeus.mockImplementation is not a function
I've looked at other reported errors having to do with the placement of jest.mock however, it does not seem to be the case here. Why is this error thrown but the tests pass? How can I fix it?

Jest cleaning up of Express server settings

I'm playing around with a side practice software, and I'm trying to learn how to use Jest for testing. But I get an error when it should clean up the testing suite, I suspect because of async code.
When I run the Jest CLI with --runInBand, it works great, but I want to understand and fix so it will work without the flag.
\
Example of one of the test files
const {Genre} = require('../../../models/genre');
const {mongoose} = require('../../../app');
const request = require('supertest');
let server;
describe('testing GET for genres', function() {
beforeEach( function() {
server = require('../../../app').server;
});
afterEach(async function() {
await Genre.deleteMany({});
await mongoose.connection.close();
server.close();
});
it('should create a genre',async function() {
let genre = {
name: "abcde"
};
const result = await request(server).post('/api/genres').send(genre);
return expect(result).toBeDefined();
}) ;
});
Example of the other one:
const {User} = require('../../../models/user');
const request = require('supertest');
const {mongoose} = require('../../../app');
let server;
describe('testing POST for users', function() {
beforeEach(function() {
server = require('../../../app').server;
});
afterEach(async function() {
await User.deleteMany({});
await mongoose.connection.close();
server.close();
});
it('should create a user',async function() {
let user = {
email: "testing123#gmail.com",
password: "Yoyoyoy"
};
const result = await request(server).post('/api/users').send(user);
expect(result.status).toBe(200);
expect(result.body).toHaveProperty("email", "testing123#gmail.com");
expect(result.body).toHaveProperty("isAdmin", false);
}) ;
});
And when I run npm test, On of the 2 tests fail, and I get the error that the server is already running and also the error that I tried to do stuff/log after jest was shut down. Cleary this is a sync issue, can anyone help me to understand why, and how to control this?

How to mock pg Pool with Sinon

In a previous project I mocked the mysql library with Sinon. I did this like so:
X.js:
const con = mysql.createPool(config.mysql);
...
Some other place in the project:
const rows = await con.query(query, inserts);
...
X.test.js:
const sinon = require('sinon');
const mockMysql = sinon.mock(require('mysql'));
...
mockMysql.expects('createPool').returns({
query: () => {
// Handles the query...
},
...
It worked perfectly.
In another project I am trying to mock pg, again with Sinon.
pool.js:
const { Pool } = require('pg');
const config = require('#blabla/config');
const pool = new Pool(config.get('database'));
module.exports = pool;
Some other place in the project:
const con = await pool.connect();
const result = await con.query(...
Y.test.js:
???
I can't understand how to mock connect().query(). None of the following approaches work:
1:
const { Pool } = require('pg');
const config = require('#blabla/config');
const mockPool = sinon.mock(new Pool(config.get('database')));
...
mockPool.expects('connect').returns({
query: () => {
console.log('query here');
},
});
1 results in no error but the real db connection is used.
2:
const { Pool } = sinon.mock(require('pg'));
const config = require('#blabla/config');
const pool = new Pool(config.get('database'));
pool.expects('connect').returns({
query: () => {
console.log('query here');
},
});
2 => TypeError: Pool is not a constructor
3:
const { Pool } = sinon.mock(require('pg'));
const config = require('#blabla/config');
const pool = sinon.createStubInstance(Pool);
pool.connect.returns({
query: () => {
console.log('query here');
},
});
3 => TypeError: The constructor should be a function.
Can anybody point me in the right direction with how to mock my PostgreSQL connection?
Example: I have postgres.js like this.
const { Pool } = require('pg');
const handler = {
count: async (pgQuery) => {
try {
const pool = new Pool();
const res = await pool.query(pgQuery);
return { count: parseInt(res.rows[0].counter, 10) };
} catch (error) {
// Log/Throw error here.
}
return false;
}
}
module.exports = handler;
The spec test I created on postgres.spec.js is like this.
const { expect } = require('chai');
const sinon = require('sinon');
const pgPool = require('pg-pool');
const handler = require('postgres.js');
describe('Postgres', function () {
it('should have method count that bla bla', async function () {
// Create stub pgPool query.
const postgreeStubQuery = sinon.stub(pgPool.prototype, 'query');
postgreeStubQuery.onFirstCall().throws('XXX');
postgreeStubQuery.onSecondCall().resolves({
rows: [{ counter: 11 }],
});
// Catch case.
const catcher = await handler.count('SELECT COUNT()..');
expect(catcher).to.equal(false);
expect(postgreeStubQuery.calledOnce).to.equal(true);
// Correct case.
const correct = await handler.count('SELECT COUNT()..');
expect(correct).to.deep.equal({ count: 11 });
expect(postgreeStubQuery.calledTwice).to.equal(true);
// Restore stub.
postgreeStubQuery.restore();
});
});
To stub pool.query(), you need to stub pg-pool prototype and method query.
Hope this helps.
Since you're needing to mock the returned results of a query, I think the easiest solution would be to abstract your database from the the code needing the query results. Example being, your query results are returning information about a person. Create a person.js module with specific methods for interacting with the database.
Your other code needing the person information from the database won't know or care what type of database you use or how you connect to it, all they care to know is what methods are exposed from person.js when they require it.
//person.js
const { Pool } = require('pg')
// do other database connection things here
const getPersonById = function (id) {
// use your query here and return the results
}
module.exports = { getPersonById }
Now in your tests, you mock the person module, not the pg module. Imagine if you had 20 some odd tests that all had the mock MySQL pool set up then you changed to pg, you'd have to change all of those, nightmare. But by abstracting your database connection type/setup, it makes testing much easier, because now you just need to stub/mock your person.js module.
const person = require('../person.js') //or whatever relative file path it's in
const sinon = require('sinon')
describe('person.js', function () {
it('is stubbed right now', function () {
const personStub = sinon.stub(person)
personStub.getPersonById.returns('yup')
expect(personStub.getPersonById()).to.eq('yup')
})
})
Below is a simpler approach that means the system-under-test doesn't need any special tricks.
It is comprised of two parts, though the first is "nice to have":
Use a DI framework to inject the pg.Pool. This is a better approach IMO anyway, and fits really well with testing.
In the beforeEach() of the tests, configure the DI framework to use a mock class with sinon.stub instances.
If you aren't using a DI framework, pass the mock as a Pool parameter... but DI is better ;)
The code below is TypeScript using tsyringe, but similar approaches will work fine with plain JavaScript etc.
Somewhere you'll have code that uses pg.Pool. A contrived example:
import { Pool } from 'pg'
...
function getPets(pool: Pool): Promise<Pet[]> {
return pool.connect()
.then(db => db.query(SQL_HERE)
.then(result => {
db.release()
return result.rows // or result.rows.map(something) etc
})
.catch(error => {
db.release()
throw error
})
)
}
That works, and it's fine if you want to pass the Pool instance in. I'd prefer not to, so I use tsyringe like this:
import { container } from 'tsyringe'
...
function getPets(): Promise<Pet[]> {
return container.resolve<Pool>().connect()
.then(...)
}
Exactly the same outcome, but getPets() is cleaner to call - it can be a pain to lug around a Pool instance.
The main of the program would set up an instance in one of a few ways. Here's mine:
...
container.register(Pool, {
useFactory: instanceCachingFactory(() => {
return new Pool(/* any config here */)
})
})
The beauty of this comes out in tests.
The code above (the "system under test") needs a Pool instance, and that instance needs a connect() method that resolves to a class with query() and release() methods.
This is what I used:
class MockPool {
client = {
query: sinon.stub(),
release: sinon.stub()
}
connect () {
return Promise.resolve(this.client)
}
}
Here's the setup of a test using MockPool:
describe('proof', () => {
let mockPool: MockPool
beforeEach(() => {
// Important! See:
// https://github.com/microsoft/tsyringe#clearing-instances
container.clearInstances()
mockPool = new MockPool()
container.registerInstance(Pool, mockPool as unknown as Pool)
})
})
The cast through unknown to Pool is needed because I'm not implementing the whole Pool API, just what I need.
Here's what a test looks like:
it('mocks postgres', async () => {
mockPool.client.query.resolves({
rows: [
{name: 'Woof', kind: 'Dog'},
{name: 'Meow', kind: 'Cat'}
]
})
const r = await getPets()
expect(r).to.deep.equal([
{name: 'Woof', kind: 'Dog'},
{name: 'Meow', kind: Cat'}
])
})
You can easily control what data the mock Postgres Pool returns, or throw errors, etc.

Writing unit tests for method that uses jwt token in javascript

I have been trying to write unit test in javascript for the method which uses jwt token validation. So the results are fetched only if the token is valid.
I want to mock the jwt token and return results. Is there any way to do it ? I tried using ava test framework, mock require, sinon but I am unable to do it.
Any thoughts ?
Code:
I am trying to mock jwt.verify
**unit test:**
const promiseFn = Promise.resolve({ success: 'Token is valid' });
mock('jsonwebtoken', {
verify: function () {
return promiseFn;
}
});
const jwt = require('jsonwebtoken');
const data = jwt.verify(testToken,'testSecret');
console.log(data)
**Error :**
ERROR
{"name":"JsonWebTokenError","message":"invalid token"}
So the issue here is that, its actually verifying the token but not invoking the mock.
Modules are singletons in Node.js. So if you required 'jwt' in your test and then it's required down in your business logic it's going to be the same object.
So pretty much you can require 'jwt' module in your test and then mock the verify method.
Also, it's important not to forget to restore the mock after the test is done.
Here is a minimal working example of what you want to accomplish (using ava and sinon):
const test = require('ava');
const sinon = require('sinon');
const jwt = require('jsonwebtoken');
let stub;
test.before(t => {
stub = sinon.stub(jwt, 'verify').callsFake(() => {
return Promise.resolve({success: 'Token is valid'});
});
})
test('should return success', async t => {
const testToken = 'test';
const testSecret = 'test secret';
const result = await jwt.verify(testToken, testSecret);
console.log(result);
t.is(result.success, 'Token is valid');
});
test.after('cleanup', t => {
stub.restore();
})

Categories

Resources