new to unit testing and the concepts of spies, stubs and mocks.
I want to test the verify
method from password.js
from code below, but I'm unable to stub
the hash
function within the test file.
As verify
uses the hash
function and the hash
function is exported, I though I should stub
the hash
function to return a fixed response rather the actually calling hash
. As I'm not trying to test the hash
function.
Problem: The created stub for hash
function is not called when testing verify
.
Side question 1: Should I be focusing on testing the logic of the function itself rather the other called functions?
Main Question: (answered) How do stub a module function that is called within the same module?
Side question 2: How would I go about stubbing hash
if it where not exported but stayed only within the module?
password.js
/**
* Creates a hash based on a salt from a given password
* if there is no salt a new salt will be generated
*
* @param {String} password
* @param {String} [salt] Optional value, if not given will generate a salt
*/
function hash (password, salt) {
// returns a promise that resolves an hash object with hash and salt key
// example: {hash: '2512521nska...', salt: '25hbBhhsfa...'}
}
/**
* Verifies if a password matches a hash by hashing the password
* with a given salt
*
* @param {String} password
* @param {String} hashString
* @param {String} salt
*/
function verify (password, hashString, salt) {
return hash(password, salt)
.then((res) => res.hash === hashString);
}
module.exports = {hash, verify};
password.test.js
import test from 'ava';
import sinon from 'sinon';
import passwordModule from './password';
test('verify - should verify password', function * (t) {
const password = 'test-password';
const salt = null;
const hash = 'my-hash';
const hashStub = sinon.stub(passwordModule, 'hash', (password, salt) => Promise.resolve({hash, salt}));
const verified = yield passwordModule.verify(password, hash, salt);
t.true(verified);
hashStub.restore();
});
Tests and the modules are transpilled with babel. But the module isn't using ES6 module exports as it is used in node env without transpilling.
I'm transpilling all code during testing so that it is future proof and the save env can be used for both frontend and backend code, where the frontend code is transpilled.
Side question 1: Should I be focusing on testing the logic of the function itself rather the other called functions?
Part of testing verify
is making sure that it calls hash
properly. Also in a more general sense, and not so much applicable to your code, is that a function should handle errors thrown by other functions properly. In your case, you're propagating any errors to the callers of verify
, that's why it doesn't really apply.
Main Question: How do stub a module function that is called within the same module?
You found an answer already, but see below for an alternative.
Side question 2: How would I go about stubbing hash if it where not exported but stayed only within the module?
A great module for this is rewire
, which allows you to overwrite private (non-exported) variables inside a module. It will also help with your "main question", because it allows you to leave the code as before.
Here is your test on rewire:
import test from 'ava';
import sinon from 'sinon';
import rewire from 'rewire';
const passwordModule = rewire('./password');
test('verify - should verify password', function * (t) {
const password = 'test-password';
const salt = null;
const hash = 'my-hash';
let hashStub = sinon.stub().returns(Promise.resolve({hash, salt}));
// Replace the `hash` function inside your module with the stub.
let revert = passwordModule.__set__('hash', hashStub);
const verified = yield passwordModule.verify(password, hash, salt);
t.true(verified);
// Revert to the original `hash` function.
revert();
});
Found out the answer for the main question within stackoverflow:
Stub module function called from the same module
To fix the issue I needed to call the hash the exported hash
function rather then the private.
exports.hash = function hash (password, salt) {
// returns a promise that resolves an hash object with hash and salt key
// example: {hash: '2512521nska...', salt: '25hbBhhsfa...'}
}
exports.verify = function verify (password, hashString, salt) {
return exports.hash(password, salt)
.then((res) => res.hash === hashString);
}
Still would like to know the answers for the side questions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With