Using Jest's test.each parameterized test variable scope - javascript

If I have a spec file like this:
let a;
beforeEach(() => {
a = 'hello';
})
describe('my test suite', () => {
test.each([
[a, 'hello']
])(
'testing %s with expected result %s',
(myVariable, expectedResult) => {
expect(myVariable).toBe(expectedResult);
})
});
I get an error that a is undefined in the parameterized table. If I use a regular test method I have access to a.

You did forget the closing bracket on the beforeEach() line.
let a;
beforeEach(() => {
a = 'hello';
} );
You also have i% and %1 which is for integers, and you want strings (%s).
With only one test, you do not need the beforeEach() and can simply do:
const a:string = 'hello';
test.each([[a, 'hello']])(
'.compare(%s, %s)',
(myVariable, expected) => {
expect(myVariable).toBe(expected);
},
);
However, I cannot get this to work either. I can reference the variable directly in the test, such as:
const a:string = 'hello';
test.each([[a, 'hello']])(
'.compare(%s, %s)',
(myVariable, expected) => {
expect(a).toBe(expected);
},
);
Using your myVariable will not get the value from a inside the closed loop of the test. Literals do work though. The beforeEach would defeat the purpose of setting a value there, as it would not need to be changed in the middle of the test.each() since this is meant to run the same test with different data. You can still create objects and other required things in your beforeEach, and reference them directly (my a variable), but the test data that changes for each run does not seem to get the value from the outside loop.

Related

When to use curly brackets vs curved brackets in React

I'm currently taking an online course to learn React and I'm confused as to when I should be using { vs (.
I have 3 files:
App.js
const App = () => {
card-list.component.jsx
const CardList = ({ monsters }) => (
card.component.jsx
const Card = ({ monster }) => {
This is the code that currently works. Notice that on the second code the last character used is (. I thought of changing it to { to make it consistent with the other files but somehow the card list no longer shows up on the page although I didn't get any compile errors.
Can someone explain this to me and tell me when I should use one over the other?
This essentially is a feature of arrow functions in js.
const myArrowFunc = ({key1}) => ("Hello there! " + key1);
is essentially the same as
const myArrowFunc = ({key1}) => { return "Hello there! " + key1 };
when you leave out the curly brackets, the return is implicit.
When you include the curly brackets, you must explicitly use the return statement.
const someObj = { key1: "value1" };
const someReturn = myArrowFunc(someObj);
console.log(someReturn); // logs "Hello there! value1" to the console
()=>{} is the syntax of arrow function
When an arrow function only contains one line that return a value, e.g.:
() => {
return 1;
}
it can be simplified to
() => 1
Now what if you want to return an object directly? i.e. how to simplify
() => {
return { foo: "bar" }
}
Since an object also use {}, you cannot write {foo: "bar"} directly after the arrow as it will be treated as the function body. Wrapping the object within () solves the problem since a () chunk must be an expression. The above example can be simplified to
() => ( { foo : "bar" } )

Change process.env variable for single test using JEST [duplicate]

This question already has answers here:
how to reset module imported between tests
(2 answers)
Closed last year.
i have the following code in a file that i'm trying to test:
foo.js
let localEnv = (process.env.LOCAL_ENVIRONMENT).toLowerCase() === 'prod' ? 'prod' : 'stage';
currently, i set this value using setupFiles which points to env.js and it contains:
process.env.LOCAL_ENVIRONMENT = 'prod';
my question is, how do i change process.env.LOCAL_ENVIRONMENT to test (or anything else) in foo.test.js? Just need to do this in a single test, so this line will be covered in my test coverage.
I tried doing something like this but it didn't work...
foo.test.js
test('Nonprod', async () => {
process.env.LOCAL_ENVIRONMENT = 'test';
...
});
You can alter the value as you tried. However you have to take into account that in your original file you only access the variable when you first load this script. In order to make this work you have to do sth like this:
// in your test file foo.test.js
const prev = process.env.LOCAL_ENVIRONMENT
process.env.LOCAL_ENVIRONMENT = 'test'; // from now on the env var is test
const myModule = require('./foo.js'); // foo.js is executed and the var is read as test
process.env.LOCAL_ENVIRONMENT = prev; // change value back
This has some caveheats as you cant test multiple scenarios with this (as the module is only loaded in once).
If you want to test more scenarios you have multiple options:
One would be to split the logic and process.env.LOCAL_ENVIRONMENT apart, for example
function getLocalEnv(env = process.env.LOCAL_ENVIRONMENT) {
return env.toLowerCase() === 'prod' ? 'prod' : 'stage';
}
This function is now very easy to test and doesn't depend on env vars for that anymore
I found the answer on how to reset and re-require the tested module here:
how to reset module imported between tests
The most easier way to test it for a specific test case is to set the test into a describe scope and to apply / remove the env value in beforeAll / afterAll or beforeEach / afterEach hooks depending on your needs.
describe('Test example', () => {
describe('prod (default)', () => {
test('do the thing', () => {
doTheThing(); // process.env.LOCAL_ENVIRONMENT is by default 'prod' because of your setupFiles
});
});
describe('test env', () => {
const oldEnv = process.env.LOCAL_ENVIRONMENT; // or { ...process.env } to copy the whole env
beforeAll(() => {
process.env.LOCAL_ENVIRONMENT = 'test';
});
afterAll(() => {
process.env.LOCAL_ENVIRONMENT = oldEnv; // do not forget to do this
});
test('do the thing', () => {
doTheThing(); // process.env.LOCAL_ENVIRONMENT is 'test' here
});
});
});

How do I parameterize test cases in TypeScript unit tests?

I have a simple passing unit test:
describe("Example", () => {
it("Return digits or empty string", () => {
let actual = SmokeTest.ProblemOne("1");
assert.equal(actual, "1");
})
})
I now wish to test many other cases (empty string, more complex inputs and edge-cases). I have read about how to implement fixtures in TypeScript but this seems like overkill for an array of test-input, expected-output values.
What is the idiomatic way to define such an array in TypeScript / Mocha?
You can just make a function that you can reuse.
function assertProblemOne(input: string, expected: string): void {
let actual = SmokeTest.ProblemOne(input);
assert.equal(expected, actual);
}
describe("Example", () => {
it("Return digits or empty string", () => {
assertProblemOne("input here", "expected result here")
})
})
However, I'm not sure that's really much better than simply.
assert.equal(SmokeTest.ProblemOne("1"), "1");
The only thing worse than debugging your code is debugging your tests. So unless there is a large amount of code that is shared between tests, I would recommend keep the code that runs in each tests as structurally simple as possible.
From the Mocha documentation:
No special syntax is required... to parameterize tests [you can] generate them with a closure.
describe("Example", () => {
// define a closure that calls the method we are testing
const testProblemOne = ({arg, expected}) =>
function () {
const actual = SmokeTest.ProblemOne(arg);
assert.equal(actual, expected);
}
// invoke the closure for each test case
it("Empty returns empty", testProblemOne({arg: "", expected: ""}));
it("Single digit identity", testProblemOne({arg: "1", expected: "1"}));
// additional test cases here...
})
This technique avoids using foreach in favor of readability and broader IDE integration.

How to get returned values from my listener's callback ES6 way

I made an input that let me filter a table of softwares.
<input type="text" id="softwares-search" class="form-control" aria-label="Input de recherche" aria-describedby="softwares-search">
Then in javascript my filter work well if I console.log(....)
But when I replace it with a return, nothing is returned. I think it is due to my var affectation through the event listener :
const maxwell = () => {
search = document.querySelector('#softwares-search').value;
return softwares.filter(row => row.name.includes(search) || row.description.includes(search));
}
const softwaresSearch = document.querySelector('#softwares-search');
if (softwaresSearch) {
var results = softwaresSearch.addEventListener('keyup', maxwell)
console.log(results);
}
Thank all
EDIT 1 :
I was so angry, so blind, I had S#!t in my eyes, no need to use a global :(
const softwaresSearch = document.getElementById('softwares-search');
if (softwaresSearch) {
softwaresSearch.addEventListener('keyup', (e) => {
search = document.getElementById('softwares-search').value;
var filtredSoftwares = softwares.filter(e => e.name.includes(search) || e.description.includes(search) );
renderTable(filtredSoftwares);
});
}
const renderTable = (softwares) => {
Object.values(softwares).forEach(value=>{
console.log(value);
});
// Todo build HTML table
}
Instead of returning I think you just need to replace the current array like this
const maxwell = () => {
search = document.querySelector('#softwares-search').value;
softwares = softwares.filter(row => row.name.includes(search) || row.description.includes(search));
}
And results is not needed:
const softwaresSearch = document.querySelector('#softwares-search');
if (softwaresSearch) {
softwaresSearch.addEventListener('keyup', maxwell)
}
As far as I know, softwareSearch.addEventListener won't return anything, since that is an event listener, and does not return any value. It simply executes the function passed in the 2nd parameter. You could try doing this instead
softwaresSearch.addEventListener('keyup', () => {
var results = maxwell();
console.log(results);
});
What this would do is that, it would call your maxwell function when the keyup event, since that is what it looks you are trying to do.
Please share all relevant code before posting a question, this code includes the variable "softwares" that exist outside what is visible to us.
Additionally, there are some issues with your code.
I don't understand your naming of the maxwell function. You should name functions as verbs, not anything else. A function is a machine that is doing something, and possibly returning something. It should be named to what it is doing.
On the second line, you say "search = ... ", but you didn't declare it as a variable.
You are returning something based on a value that isn't validated ('search' can be either undefined or a string value in this case), hence, your return will most likely just return undefined and not any value at all.
Your function can possibly not return anything at all since you are returning something within your if-statement. You can use a closure to always return something.
I would also suggest passing a search string as a variable to your function that should return a list based on the search query. Getting in the habit of short, concise functions with expected inputs/outputs, will make your code more readable and less error-prone and less likely to produce unwanted side-effects.
I don't know the rest of your code, but I don't recommend assigning variables in the global scope. Your "maxwell", "softwareSearch" variables both exist in the global space, unless you have wrapped them in another function block already (such as jquerys $(document).ready(() => { ...everything here is scoped })
You are getting the same element in two different places in your code.
Here is an updated code sample, but I can't test it since I don't know the rest of your code.
/*
* Executing the whole thing in this IIFE will make all variables declared inside here scoped to this block only,
* thus they can't interfere with other code you may write
*/
(() => {
const listOfSoftwares = softwares; // --- get your softwares variable here somehow, I don't know where "software" comes from.
// input element
const search = document.querySelector('#softwares-search');
/**
* Filter search results
* #param {string} query Search query
* #returns {Array} The results array
*/
const filterSoftwareSearchResults = (query) => {
let results = [];
results = listOfSoftwares.filter(software => software.description.includes(query) || software.title.includes(query))
// Verify
console.log(results);
// Should return array of results, even if empty
return results;
}
if (search) {
search.addEventListener('keyup', () => {
filterSoftwareSearchResults(search.value)
})
}
})()
The addEventListener function always returns undefined, so your results variable is undefined.
Returning from the callback function (maxwell) is also of no use.
You either need to do something with the data inside of your callback, or maybe pass the data to a global variable.

Jest - Get Test and Description Name [duplicate]

This question already has answers here:
Get the current test/spec name in Jest
(4 answers)
Closed 7 months ago.
I am trying to get the name of the test name that was passed into it(name: string) Jest function, as well as the description name that was passed into describe(name: string, ....).
Is there a way to access this somehow?
E.g.
describe("Description", () => {
it("Test", () => {
const description = "How do I get the description name?"
const test = "How do I get the test name?";
expect(`${description} - ${test}`).toBe("Description - Test");
});
})
CodeSandbox Link
Update 1
Underlying reason why I want this is because I want to do some custom snapshot naming conventions based on describe, test, and some environment variables (e.g. viewport sizes + mock/real data).
You can achieve this using a custom matcher
expect.extend({
testName(exp) {
return {
pass: this.currentTestName === exp,
message: () => '',
};
},
});
describe("Description", () => {
it("Test", () => {
expect(`Description Test`).testName();
});
});
Perhaps, the most obvious solution is what you want. Instead of passing literals to describe and it just store them in a respective constants which could then be easily accessed inside the respective test cases though the outer scope.
These constants could be easily initialized either by values from the environmental variables or by literal defaults.
const suiteA = 'Description';
describe(suiteA, () => {
const testA = 'Test';
it(testA, () => {
expect(`${suiteA} - ${testA}`).toBe("Description - Test");
});
})

Categories

Resources