jest: How to teardown after (just) an individual test - javascript

jest provides afterEach, beforeEach, afterAll and beforeAll to complete setup and teardown logic. What I would like to do, is to clear up after one particular test. Consider the following:
describe("a family of tests it makes sense to group together", () => {
...
test("something I want to test", () => {
// some setup needed for just this test
global.foo = "bar"
// the test
expect(myTest()).toBe(true)
// clear up
delete global.foo
}
...
}
The problem with the above...
If the test above fails for some reason, then delete global.foo is never run. This means that potentially all of the tests following it will fail. Rather than seeing 1 test fail, I see a whole load of tests fail, which could be confusing.
Potential (non-ideal) solutions
One solution is just to add delete global.foo into my afterEach. It doesn't really need to be run after every test, but it doesn't do any harm either. Another solution would be to put the particular test by itself so that afterEach would only apply to it. But this doesn't seem ideal either - if that test belongs with other tests, it aught to be possible for it to remain with them.
My question:
Is there a way to run teardown logic for just a specific test (without running it inside the actual test). In my particular use-case the first outlined solution is fine, but I can imagine there might be situations where finer grained control is needed. If my teardown method took a long time for example I wouldn't want to repeat it lots, as this would slow down the whole test-suite.

In many cases tests can share a common afterEach clean-up even if it's needed for one of them, as long as it doesn't affect others.
Otherwise, this is what block structure is responsible for. One or several tests can be grouped with nested describe just to have their own afterEach, etc blocks, and the only downside is that it makes the report less pretty:
describe("a family of tests it makes sense to group together", () => {
...
describe("something I want to test", () => {
beforeEach(() => {
global.foo = "bar"
});
test("something I want to test", () => {
expect(myTest()).toBe(true)
}
afterEach(() => {
delete global.foo
});
});
beforeEach and afterEach can be desugared to try..finally:
test("something I want to test", () => {
try {
global.foo = "bar"
expect(myTest()).toBe(true)
} finally {
delete global.foo
}
})
This also allows for asynchronous tests but requires them to be written with async instead of done.

I know that this is an old question, but for anyone who stumbles upon this challenge in the future, here's a small library I wrote way back when, called jest-after-this that does just that:
import { afterThis } from 'jest-after-this';
it('should do something that requires a cleanup', () => {
global.foo = 'something';
afterThis(() => {
delete global.foo;
});
// ... rest of test here can fail, the cleanup method will run anyways
});
Hope this helps :)

Related

How to pass a value from one Jasmine JavaScript test to another test

I am testing reading and writing (to a server, to a mongo db).
I know, I am not supposed to do this, I should be using mocks, ... but anyhow
I want to write a document, read that document to make sure it is was correctly written,
then delete that document, then verify it is gone. So I have 2 problems that I have solved but by using 2 hacks.
1) how do I pass along the mongo _id of the document from step to step. I'd like to have a simple variable in my Jasmine code that I can read and write from each test. I am hacking it now by creating a variable in the actual Angular module that I am testing, and reading and writing a variable over in that code.
2) since I have to wait for each IO operation before proceeding, I am taking advantage of the
setTimeout(() => {done();}, 2000); feature in a set of nested beforeEach(function(done){
sections.
I would like to learn simple, better ways of doing these if there are any.
thanks
What you're doing is called integration tests. Nothing wrong with doing them, but I usually write integration tests using Angular's E2E facilities.
That said, just save the value in a global variable and it will change each test. Some psuedo code
describe('integration test', () => {
let id;
it('should create a document', () => {
// code to create item and return id
id = _id;
}
it('should load document', () => {
console.log(id); // should be value from create test
}
it('should delete document, () => {
console.log(id); // should have value from create test
}
}
Since the id value is never set in a beforeEach() it will retain its value between tests in the same describe() block.
I have cautions about this when writing unit tests--because the tests must run in a specific order to execute. But, the desire is that E2E / integration tests are run sequentially.

How to test a method with setTimeout and jquery using jest

I'm struggling a bit to find a solution on how to test this exported function with jest.
export const scrollToError = () => {
setTimeout(() => {
const hasErrorElement = jQuery('.has-error');
if (!hasErrorElement.length) return;
jQuery('html,body').animate({
scrollTop: hasErrorElement.offset().top - 50,
}, 'slow');
}, 400);
};
I imported it in my test file and tried to start it:
import { scrollToError } from './utils';
describe('Utils', () => {
it('should scroll to error', () => {
const result = scrollToError();
expect(result).toBe(true); //added this just to force an error and got result as undefined
});
});
Could anyone give me any tips on how to test code with these dependencies?
scrollToError() is asynchronous function and you can't invoke it and expect the result to be there immediately. You need to wait that amount of ms (400 in your case), before testing for it.
Asynchronous code is tested a bit differently in Jest: Testing Asynchronous Code. You can also take control over the timers or combine it all with the manual mocks and override jQuery itself.
How are you using jQuery?
I mean, did you get it using npm or yarn? to mock node_modules you can follow this link: https://jestjs.io/docs/en/manual-mocks#mocking-node-modules
Otherwise, you will have to create a manual mock. You can see how to do it here: https://jestjs.io/docs/en/manual-mocks
Updated:
the simplest way is to override it, is while settting up your test at beforeXXX method.
You can simply put something like window.JQuery = jest.fn();
this is the simplest mock ever but you will have to create the methods like animate and other jquery related methods.
Having second thoughts here and looking to your function, if you mock jQuery what else left to be tested?
If you mock, you will be testing if your fn are doing the steps you defined here. Like check if the jQuery fn was called with .has-error class or if animate received the correct parameters.
This kind of test doesn't help you at all, it's just checking if it's following line by line your algorithm. The problem here, that you could do some refactorings like changing the .has-error class name or the animate method by other improved one.
What you really need to change, if it's doing at the end what should be doing. Displaying the div or whatever that should be displayed. If you test that, regardless the way you refactor your code the test will check if the final solution still works and that what matters.
Was I clear? English is not my first language so, it may be a little bit confusing
I finally managed to find a proper solution.
I wrote three test cases for it:
jest.useFakeTimers();
describe('utils', () => {
afterEach(() => {
document.body.innerHTML = '';
});
it('ScrollToError - should run the settimeout for 400 ms', () => {
scrollToError();
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 400);
});
it('ScrollToError - should scroll to error', () => {
document.body.innerHTML = formStep1ErrorMock;
window.setTimeout = fn => fn();
const result = scrollToError();
expect(result).toBe(true);
});
it('ScrollToError - should do nothing as has no errors', () => {
document.body.innerHTML = formStep1Mock;
window.setTimeout = fn => fn();
const result = scrollToError();
expect(result).toBe(true);
});
});
So basically, first I check if the setTimeout was called with the proper amount of seconds (not that it's important).
Then I mock the setTimeout by doing this window.setTimeout = fn => fn(); so it runs without waiting for the delay. I also mock the html with the proper details I need it to have.
And finally, I just cover another scenario.
PS: I added a return true statement to the scrollToError method to make it simpler to have an expected result.
This way I achieved 100% coverage for this method.

How to handle global objects / tests influencing other tests in Jest

Some of my tests in my React Native project affect global objects. These changes often affect other tests relying on the same objects.
For example: One test checks that listeners are added correctly, a second test checks if listeners are removed correctly:
// __tests__/ExampleClass.js
describe("ExampleClass", () => {
it("should add listeners", () => {
ExampleClass.addListener(jest.fn());
ExampleClass.addListener(jest.fn());
expect(ExampleClass.listeners.length).toBe(2);
});
it("should remove listeners", () => {
const fn1 = jest.fn();
const fn2 = jest.fn();
ExampleClass.addListener(fn1);
ExampleClass.addListener(fn2);
expect(ExampleClass.listeners.length).toBe(2);
ExampleClass.removeListener(fn1);
expect(ExampleClass.listeners.length).toBe(1);
ExampleClass.removeListener(fn2);
expect(ExampleClass.listeners.length).toBe(0);
});
});
The second test will run fine by itself, but fails when all tests are run, because the first one didn't clean up the ExampleClass. Do I always have to clean up stuff like this manually in each test?
It seems I'm not understanding how the scope works in Jest... I assumed each test would run in a new environment. Is there any documentation about this?
Other examples are mocking external libraries and checking if the mocked functions in them are called correctly or overriding Platform.OS to ios or android to test platform-specific implementations.
As far as I can see, the scope of a test is always the test file. For now I changed my code to have a beforeEach callback that mainly calls jest.resetAllMocks() and resets Platform.OS to its default value.
Fast and sandboxed
Jest parallelizes test runs across workers to maximize performance. Console messages are buffered and printed together with test results. Sandboxed test files and automatic global state resets for every test so no two tests conflict with each other.
https://facebook.github.io/jest/

How to skip to next next describe on error in Mocha?

I have a bunch of describes that test different parts of an API. In one section, all the tests are dependent on one test succeeding. I want to make Mocha run the first test, and if it fails, skip all following tests and run the next test suite for the next section of the API.
mocha --bail would stop testing altogether after the first fail, and won't continue to the next section.
mocha-steps is a viable solution, but I prefer not to use any external libraries. In addition, it doesn't skip steps after the failure, it doesn't print them altogether. Like I said, it's a viable solution, but not ideal.
What would be the best way to implement this behavior in vanilla Mocha?
Put what you call your "first test" in a before hook inside a describe block that contains all the other tests:
describe("bunch of related tests", function () {
before(function () {
// "first test" here
});
it("test 1", function () { ... });
it("test 2", function () { ... });
// ...
});
This is the proper way in "vanilla Mocha" to set a dependency between the code in the before hook and each of the tests. If the before hook fails, Mocha will report it, and it will skip all the tests in the describe block. If you have other tests elsewhere, they will still run.
Although I up-voted the accepted answer, I wasn't able to get a Mocha it test to run inside a before function. Instead I had to separate the first test into its own describe and set a variable if the test passed, then check the variable in the before of the describe containing all the other tests.
let passed = false
describe('first test', function() {
it('run the first test', function(done) {
if (theTestPassed)
passed = true
done()
})
})
describe('rest of the tests', function() {
before(function() {
if (!passed)
throw new Error('skip rest of the tests')
});
it('second test', ...)
it('third test', ...)
});

Conditional mocha test

I use mocha for some integration testing and have many test sets.
Each set has initialization tests. When such tests fail, the rest of the set should not run at all, because if one fails then each will fail.
The thing is that I can't avoid such initialization tests, because part of the code/environment is generated by some tool which does not guarantee any correct result.
Is it possible to implement this using mocha ?
Using the BDD interface, the normal way to do this with Mocha is to put anything that sets up the testing environment into before or beforeEach:
describe("foo", function () {
describe("first", function () {
before(function () {
// Stuff to be performed before all tests in the current `describe`.
});
beforeEach(function () {
// Stuff to perform once per test, before the test.
});
it("blah", ...
// etc...
});
describe("second", function () {
before(function () {
// Stuff to be performed before all tests in the current `describe`.
});
beforeEach(function () {
// Stuff to perform once per test, before the test.
});
it("blah", ...
// etc...
});
});
If the before or beforeEach that a test depends on fails, then the test is not run. Other tests that don't depend on it will still run. So in the example above if the callback passed to before in the describe named first fails, the tests in the describe named second won't be affected at all and will run, provided that their own before and beforeEach callbacks don't fail.
Other than this, Mocha is designed to run tests that are independent from each other. So if one it fails, then the others are still run.
I found mocha-steps which basically allow you to write a "chain" of it()s (called step()) and mocha will abort the suite if one of them breaks, thus avoiding the cascade of inevitable failures, and I found pull request 8 marks subsequent steps and subsuites as pending. So I can write:
describe("businessCode()", function() {
step("should be not null", function() {
assert(businessCode() != null)
});
step("should be a number", function() {
assert(typeof businessCode() === 'number');
});
step("should be greater than 10", function() {
assert(businessCode() > 10);
});
describe("thingThatCallsBusinessCode()", function() {
step("should be greater than 10", function() {
assert(thingThatCallsBusinessCode() != null);
});
});
});
If e.g. businessCode() returns a boolean, only the should be a number test will fail; the subsequent ones (and the subsuite will be marked as pending).

Categories