Unit testing: Input/output - javascript

I am trying to run a unit test for the following code: This code below only checks if the function is true.
Here is the Unit testing code:
import {encodeLengthHex, toHex } from './utils';
// testing new function
describe('EncodeLengthHex', () => { //mocking the method
test('Testing Function toHex ', () => { // declaring the method
const str = 128 // initial value
const actual = encodeLengthHex(str) // calculate the value
expect(actual).toMatchSnapshot(); // checking whether is true
})
});
Here is the main code:
export function encodeLengthHex(n) {
if (n <= 127) {
return toHex(n);
}
const nHex = toHex(n);
const lengthOfLengthByte = 128 + nHex.length / 2;
return toHex(lengthOfLengthByte) + nHex;
Function is passing. But how could I add a few inputs to test and compare when this function pass or fail. Thanks

Related

Jest testing, keeps return undefined?

I'm trying to test a function with jest, and I simply can figure out what's wrong? It keeps saying it expects to return the output, but got undefined. I have tested the function elsewhere where it seems to return the correct array.
I'm calling my my function and passing it an Object, it's then supposed to return an array. Then I'm calling .toEqual(output) which is an array.
//This is my function
const allAddresses = [
];
const updateAllAddresses = (obj) => {
const transferAmount = obj.transferAmount;
const to = obj.to;
const transferAddress = obj.address;
const newBalance = obj.newBalance;
const addressArr = [...allAddresses];
console.log("This addressArr", addressArr);
console.log("this is obj", obj);
//To set your account to the new balance after transfer and
//to check if the address you transfer to is your own account
addressArr.map((address) => {
if (address.account === transferAddress) {
console.log("This is inside the map !!!!");
address.balance = Number(newBalance);
}
if (address.account === to) {
console.log("2");
address.balance = Number(transferAmount) + Number(address.balance);
}
console.log("last part of the testing", addressArr);
return addressArr;
});
};
const obj = {
};
const output = [
];
//This is my test
describe("Update array", () => {
test("update the array with the new information", () => {
expect(updateAllAddresses(obj)).toEqual(output);
});
});
You cannot short circuit and return inside a map function. You should return the object after the map
Also, when you change address inside the map; It really does not change anything, since that address variable will be removed from memory on next iteration
There is a problem with your updateAllAddresses method.
You are not returning anything then the result of your function becomes undefined;
add return to where you are using .map method.
return addressArr.map((address) => {
if (address.account === transferAddress) {
console.log("This is inside the map !!!!");
address.balance = Number(newBalance);
}
if (address.account === to) {
console.log("2");
address.balance = Number(transferAmount) + Number(address.balance);
}
console.log("last part of the testing", addressArr);
return address;
});

How to generate new random value for each scenario in protractor node js

Am able to generate a random string in protractor node js BDD framework.But the problem is, the same value is being hold for all the scenarios when batch execution is triggered and the tests are getting failing because of the same value used in all the scenarios.Can someone please suggest how to generate new random value everytime a scenario is invoked during batch execution and the same value should be passed for all the methods for that scenario.
Note : I have declared it as global variable to be used across the methods for a particular scenario.
this
// 1.spec.js
let generateString = (length) => {
let letters = 'abcdefghijklmnopqrstuvwxyz',
str = '';
for (let i = 0; i < length; i++) {
str += letters[Math.floor(Math.random() * letters.length)];
}
return str;
}
let randomString = generateString(7);
describe(`Scenario`, () => {
it('1', async () => {
console.log(randomString);
});
it('2', async () => {
console.log(randomString);
});
it('3', async () => {
console.log(randomString);
});
});
will produce this
Jasmine started
saxijqt
Scenario
✓ 1 (0.014 sec)
saxijqt
✓ 2 (0.035 sec)
saxijqt
✓ 3 (0.01 sec)

Wait for all Firebase data query requests before executing code

I am trying to fetch data from different collections in my cloud Firestore database in advance before I process them and apply them to batch, I created two async functions, one to capture the data and another to execute certain code only after all data is collected, I didn't want the code executing and creating errors before the data is fetched when i try to access the matchesObject after the async function to collect data is finished, it keeps saying "it cannot access a property matchStatus of undefined", i thought took care of that with async and await? could anyone shed some light as to why it is undefined one moment
axios.request(options).then(function(response) {
console.log('Total matches count :' + response.data.matches.length);
const data = response.data;
var matchesSnapshot;
var marketsSnapshot;
var tradesSnapshot;
var betsSnapshot;
matchesObject = {};
marketsObject = {};
tradesObject = {};
betsObject = {};
start();
async function checkDatabase() {
matchesSnapshot = await db.collection('matches').get();
matchesSnapshot.forEach(doc => {
matchesObject[doc.id] = doc.data();
console.log('matches object: ' + doc.id.toString())
});
marketsSnapshot = await db.collection('markets').get();
marketsSnapshot.forEach(doc2 => {
marketsObject[doc2.id] = doc2.data();
console.log('markets object: ' + doc2.id.toString())
});
tradesSnapshot = await db.collection('trades').get();
tradesSnapshot.forEach(doc3 => {
tradesObject[doc3.id] = doc3.data();
console.log('trades object: ' + doc3.id.toString())
});
betsSnapshot = await db.collection('bets').get();
betsSnapshot.forEach(doc4 => {
betsObject[doc4.id] = doc4.data();
console.log('bets object: ' + doc4.id.toString())
});
}
async function start() {
await checkDatabase();
// this is the part which is undefined, it keeps saying it cant access property matchStatus of undefined
console.log('here is matches object ' + matchesObject['302283']['matchStatus']);
if (Object.keys(matchesObject).length != 0) {
for (let bets of Object.keys(betsObject)) {
if (matchesObject[betsObject[bets]['tradeMatchId']]['matchStatus'] == 'IN_PLAY' && betsObject[bets]['matched'] == false) {
var sfRef = db.collection('users').doc(betsObject[bets]['user']);
batch11.set(sfRef, {
accountBalance: admin.firestore.FieldValue + parseFloat(betsObject[bets]['stake']),
}, {
merge: true
});
var sfRef = db.collection('bets').doc(bets);
batch12.set(sfRef, {
tradeCancelled: true,
}, {
merge: true
});
}
}
}
});
There are too many smaller issues in the current code to try to debug them one-by-one, so this refactor introduces various tests against your data. It currently won't make any changes to your database and is meant to be a replacement for your start() function.
One of the main differences against your current code is that it doesn't unnecessarily download 4 collections worth of documents (two of them aren't even used in the code you've included).
Steps
First, it will get all the bet documents that have matched == false. From these documents, it will check if they have any syntax errors and report them to the console. For each valid bet document, the ID of it's linked match document will be grabbed so we can then fetch all the match documents we actually need. Then we queue up the changes to the user's balance and the bet's document. Finally we report about any changes to be done and commit them (once you uncomment the line).
Code
Note: fetchDocumentById() is defined in this gist. Its a helper function to allow someCollectionRef.where(FieldPath.documentId(), 'in', arrayOfIds) to take more than 10 IDs at once.
async function applyBalanceChanges() {
const betsCollectionRef = db.collection('bets');
const matchesCollectionRef = db.collection('matches');
const usersCollectionRef = db.collection('users');
const betDataMap = {}; // Record<string, BetData>
await betsCollectionRef
.where('matched', '==', false)
.get()
.then((betsSnapshot) => {
betsSnapshot.forEach(betDoc => {
betDataMap[betDoc.id] = betDoc.data();
});
});
const matchDataMap = {}; // Record<string, MatchData | undefined>
// betIdList contains all IDs that will be processed
const betIdList = Object.keys(betDataMap).filter(betId => {
const betData = betDataMap[betId];
if (!betData) {
console.log(`WARN: Skipped Bet #${betId} because it was falsy (actual value: ${betData})`);
return false;
}
const matchId = betData.tradeMatchId;
if (!matchId) {
console.log(`WARN: Skipped Bet #${betId} because it had a falsy match ID (actual value: ${matchId})`);
return false;
}
if (!betData.user) {
console.log(`WARN: Skipped Bet #${betId} because it had a falsy user ID (actual value: ${userId})`);
return false;
}
const stakeAsNumber = Number(betData.stake); // not using parseFloat as it's too lax
if (isNaN(stakeAsNumber)) {
console.log(`WARN: Skipped Bet #${betId} because it had an invalid stake value (original NaN value: ${betData.stake})`);
return false;
}
matchDataMap[matchId] = undefined; // using undefined because its the result of `doc.data()` when the document doesn't exist
return true;
});
await fetchDocumentsById(
matchesCollectionRef,
Object.keys(matchIdMap),
(matchDoc) => matchDataMap[matchDoc.id] = matchDoc.data()
);
const batch = db.batch();
const queuedUpdates = 0;
betIdList.forEach(betId => {
const betData = betDataMap[betId];
const matchData = matchDataMap[betData.tradeMatchId];
if (matchData === undefined) {
console.log(`WARN: Skipped /bets/${betId}, because it's linked match doesn't exist!`);
continue;
}
if (matchData.matchStatus !== 'IN_PLAY') {
console.log(`INFO: Skipped /bets/${betId}, because it's linked match status is not "IN_PLAY" (actual value: ${matchData.matchStatus})`);
continue;
}
const betRef = betsCollectionRef.doc(betId);
const betUserRef = usersCollectionRef.doc(betData.user);
batch.update(betUserRef, { accountBalance: admin.firestore.FieldValue.increment(Number(betData.stake)) });
batch.update(betRef, { tradeCancelled: true });
queuedUpdates += 2; // for logging
});
console.log(`INFO: Batch currently has ${queuedUpdates} queued`);
// only uncomment when you are ready to make changes
// batch.commit();
}
Usage:
axios.request(options)
.then(function(response) {
const data = response.data;
console.log('INFO: Total matches count from API:' + data.matches.length);
return applyBalanceChanges();
}

Can I make a stub for a range of outcomes? "sinon.stub().callsFake(()=>{})" AssertionError: NaN

From my understanding, a stub is a spy who is forced to take action, such as picking a side(i.e. throw). Can I /or does it makes sense to fake returning a range of number?
I am trying to test a "Loan.prototype.cost", which calls "Loan.prototype.payment", then does some calculation.
In the commented out part of my snippet, I tried to create a range of number(the "const rdnPaymentReturn" from 85.61 to 85.63), and fake the "payment" property to be this range(const paymentSpy). But I would get error saying " AssertionError: expected NaN to be within 27.32..27.56"
If I can/or it makes sense to set "paymentSpy" to a range of number(85.61-85.63), how do I resolve this AssertionError?
Ref:
beforeEach(function() {
l = new Loan();
});
describe('#cost()', function() {
it('should call #payment() and return the correct cost amount', function() {
l.principal = 1000;
l.term = 1;
l.rate = 0.05;
sinon.stub(l, 'payment').callsFake(() =>{ return 85.61; });
expect(l.cost()).to.equal(27.32);
// const rdnPaymentReturn = () =>{
// return Math.random() * (85.63 - 85.61) + 85.61;
// }
//const paymentSpy = sinon.stub(l, 'payment').callsFake(() =>{ return rdnPaymentReturn; });
//expect(l.cost()).to.be.within(27.32, 27.56);
});
});
This isn't quite right:
const paymentSpy = sinon.stub(l, 'payment').callsFake(() =>{ return rdnPaymentReturn; });
It is saying: stub payment with a function that when called returns a reference to the function rdnPaymentReturn. But rdnPaymentReturn is never called.
So you need to either return the result of calling the function:
const paymentSpy = sinon.stub(l, 'payment').callsFake(() => {
return rdnPaymentReturn()
});
or just pass in the function to call:
const paymentSpy = sinon.stub(l, 'payment').callsFake(rdnPaymentReturn);

Running a repeated mocha test for a function

I am new mocha and I need to run one test 100 times. In the following code, a function regexTest is tested for true.
var assert = require('assert');
describe('regexTest',function() {
it('should return true if the string starts with a number',function() {
var i = 1;
while(i++ <= 100) {
assert.equal(true,regexTest(Math.random().toString(36).substring(7)));
}
});
})
function regexTest(randStr) {
let match = randStr.match(/^\d+/);
if(match && match.length > 0) {
return true;
}else{
return false;
}
}
I am not sure if this is the correct way to run a repeated test, inside a while loop. Also, I doubt the results because no matter what, it always returns passed for a single test.
What could be a better approach here?
So first off , your code seems correct and running it locally returns:
~/P/test ➤ node_modules/.bin/mocha x.js
regexTest
1) should return true if the string starts with a number
0 passing (10ms)
1 failing
As far as I remember there is no official way to repeat an assert statement, so running it inside a loop should be fine.
You might want to consider adding a more verbose failure message to your assert statement though, something like:
while(i++ <= 100) {
let testStr = Math.random().toString(36).substring(7);
assert.equal(true, regexTest(testStr), 'Test failed on ' + testStr);
}
(and just in case it helps, you don't need that if block in your regexTest function. Simply end with return match && match.length)
If you assert at every single execution of the loop, test will end at first assertion error. If you want to see how many times your test failed out of 100, you can store test results in an array, you can do something like:
var assert = require('assert');
describe('regexTest',function() {
it('should return true if the string starts with a number',function() {
var i = 1;
var errorsArray = [];
while(i++ <= 100) {
var randomNumber = Math.random();
if(!randomNumber.toString(36).substring(7)){
errorsArray.push(randomNumber);
}
}
assert.equal(0, errorsArray.length, errorsArray);
});
})
The problem with the code you have is that you are actually only testing the last random string generated.
Also, as mentioned by #Geotob, it will nicer if you print the string on which the test case failed.
You could frame it like:
var assert = require('assert');
function makeTest() {
describe('regexTest', function () {
var i = 0;
while (i++ <= 20) {
it('should return true if the string starts with a number', function () {
var i = 0;
let str = Math.random().toString(36).substring(7);
let output = regexTest(str);
assert.equal(true, output, `Failed on ${str}`);
});
}
});
}
function regexTest(randStr) {
let match = randStr.match(/^\d+/);
if (match && match.length > 0) {
return true;
} else {
return false;
}
}
makeTest();
This should work fine.
Notes:
Dynamically generating tests from the mocha doc.

Categories

Resources