I am creating a series of routes to build a quiz of sorts. I've decided to build it pretty much like a linked list with each element in the form pointing to the next/prev elements. I want everything to have tests, but I can't figure out how to write a test for the below:
async create(next){
// check if form already has elements added to it
const elements = await db.elements.findAll({ where: { form_id: this.formId } });
// if form already has elements added and next is not provided, append element to the end of the form
if (elements.length && !next) {
await this._createAtEnd(elements, next);
}
// if next is not the end of the element list, insert element before next element
else if (elements.length){
await this._insert(elements, next);
}
return db.elements.findAll({ where: { form_id: this.formId } });
}
async _insert(elements, next){
const tempPrevious = elements.filter(el => el.next_element_id === next).pop();
const tempNext = tempPrevious ? elements.filter(el => el.id === tempPrevious.next_element_id).pop() : null;
const created = await db.elements.create({
form_id: this.formId,
previous_element_id: tempPrevious ? tempPrevious.id : null,
next_element_id: next,
});
// update the element prior to the newly inserted element
if (tempPrevious) {
return Promise.all( [
tempPrevious.update({ next_element_id: created.id }),
tempNext.update({ previous_element_id: created.id }),
]);
}
// element is being inserted at the beginning
const first = elements.map(el => el.previous_element_id === null).pop();
return first.update({ previous_element_id: created.id });
}
Let's say I stub out the database (db) so that the test looks like the following:
it('should create a new element', async () => {
const formId = 1;
const next = 2;
const data = [
{ id: 0, form_id: formId, previous_element_id: null, next_element_id: 1, },
{ id: 1, form_id: formId, previous_element_id: 0, next_element_id: 2, },
{ id: 2, form_id: formId, previous_element_id: 1, next_element_id: null, },
];
data.forEach(el => el.update = function(args){fakeDb.update(this, args)})
const fakeDb = {
findAll: async (args) => {
if (args.where) {
const keys = Object.keys(args.where);
return data.filter(el => {
return keys.filter(key => el[key] === args.where[key]).length;
});
}
return data;
},
create: async (item) => {
item.id = data[data.length - 1].id + 1;
data.push(item);
return item;
},
update: (obj, args) => {
const keys = Object.keys(args);
for (let i=0; i< data.length; i++){
if (data[i].id === obj.id) {
keys.forEach(key => data[i][key] = args[key]);
break;
}
}
},
};
sinon.stub(db.elements, 'create').callsFake(fakeDb.create);
sinon.stub(db.elements, 'findAll').callsFake(fakeDb.findAll);
const newElement = new NewElement({ formId });
const created = await newElement.create(next);
expect...
Looking at this I feel like I'm writing way to much code for fakeDb and not actually testing create. My initial feeling was to return a constant value, like:
const fakeDb = {
create: Promise.resolve({ id: 1, forms_id: formId, previous_element_id: 0, next_element_id: 2 }),
};
However, then I really felt like the test was useless? The function would still be able to pass regardless of what I did internally. Is it customary in testing to create a data set you can manipulate, even if you're then creating a bunch of methods to emulate the db?
I feel good that I could take the fakeDb obj I wrote out of this test and create some kind of helper function so I'm not rewriting it in each test. I also don't want to overly complicate the whole thing either.
Aucun commentaire:
Enregistrer un commentaire