How do I fail a test?
Failing a test in Cypress and Mocha can be done in a myriad of ways but I am assuming you want to fail the test and log something meaningful as opposed to, say, cause a stack overflow error with an infinite while loop.
The easiest and most straight forward way to fail a test in Cypress is to do one of the following from your inside a Mocha hook, suite, or test function (a Mocha it
, context
, describe
, before
, beforeAll
, after
, or afterAll
block):
assert(false, 'We failed our test!')
throw new Error('We failed our test!)
How do I skip a test?
Skipping a test can also be done in a myriad of ways. One non-programmatic way of doing this is to simply prepend the letter x
to any Mocha test or test suite function. Thus:
it(...)
becomes xit(...)
context(...)
becomes xcontext(...)
describe(...)
becomes xdescribe(...)
This works because these are the internal names that Mocha uses to mark a test or test suite as pending.
The problem with this approach is it requires the tests be renamed when what most people want to do is skip a test or test function at runtime based on some condition.
How do I skip a test programmatically?
Luckily for us Mocha ships with the ability to skip a test or test suite programmatically out of the box. The inclusive tests section of their docs delves deeper but basically, the rule of thumb is, it's better to skip tests programmatically than to comment them out since they will appear inside the test runner log as skipped tests, making it obvious that a test has indeed been skipped. The magic command? Simply call:
it.skip()
context.skip()
describe.skip()
Or
it.only()
context.only(...)
describe.only(...)
From within any Mocha hook or Mocha suite function. Knowing what we now know we can create a Cypress custom command that helps us achieve this task.
How can I skip a test using a custom command?
We're going absolute bare bones here and using an example command that is completely agnostic about the condition being checked. Leveraging some of JavaScript's features/design quirks (such as block scoped functions and the somewhat tricky Function.prototype.bind()
method) we can make a custom command that can be called from anywhere inside your test suite, provided it has the right context (context as in this
context):
// commands.ts
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
interface Chainable {
/**
* Custom command which will skip a test or context based on a boolean expression.
*
* You can call this command from anywhere, just make sure to pass in the the it, describe, or context block you wish to skip.
*
* @example cy.skipIf(yourCondition, this);
*/
skipIf(expression: boolean, context: Mocha.Context): void;
}
}
}
Cypress.Commands.add(
'skipIf',
(expression: boolean, context: Mocha.Context) => {
if (expression) context.skip.bind(context)();
}
);
The above command (using TypeScript) expects an expression that evaluates to a boolean (truthy) value which, if it does, will skip the given test or test suite. It does this by binding the scope of the given Mocha context to the original this
context of the test block and immediately calling the newly bound function (note the extra ()
on the end of the method - that's intentional).
How do I skip an individual test?
Now you can skip, fail, or select an individual test for execution based on whatever condition it is you are evaluating:
describe('Events', () => {
const url = `https://stackoverflow.com/question/ask`;
context('Nested context', () => {
// only this first test will be skipped
it('test', function () {
cy.skipIf(url.includes('/question/'), this);
expect(this.stackOverflow.wiki).to.be('useful');
});
// this test will run
it('test two', function () {
expect(this.stackOverflow.answer).to.be('accepted');
});
});
});
Again, the custom command in our case skips the test but you can put what we've learned to good use here and do whatever you like instead. If you're really feeling fancy you could even pass in a function as a third parameter which is conditionally executed, or not.
How do I skip a test suite from within a before hook?
Using our custom command, the same way you skip a test. Note that this will skip all of the tests within the Mocha context, so:
describe('Events', () => {
const url = `https://stackoverflow.com/question/ask`;
before(function () {
cy.visit(url);
cy.skipIf(cy.url().includes('/flow.com/'), this));
});
// all of the below tests will be skipped
context('Nested context', () => {
it('test', function () {
expect(this.stackOverflow.wiki).to.be('useful');
});
it('test two', function () {
expect(this.stackOverflow.answer).to.be('accepted');
});
});
});
How do I group tests and skip them based on some condition?
Similar to how we achieve skipping a single test or test suite, we can also skip individual tests from different test suites based on a shared property. In this example we are going to check the test title inside a global beforeEach
hook - this could be refactored into a custom command - it's just a minimal example meant as a proof of concept.
From inside your support file:
// i.e. e2e.ts
import './commands';
beforeEach(function () {
const testTitle = Cypress.currentTest.title;
if (testTitle.includes('unstable')) {
this.skip();
}
});
And from inside your specs:
describe('Events', () => {
context('Nested context', () => {
it('[api] test', function () {
expect(this.stackOverflow.wiki).to.be('useful');
});
// this test will be skipped
it('[api, unstable] test two', function () {
expect(this.stackOverflow.answer).to.be('accepted');
});
});
});
describe('More Events', () => {
context('Nested context', () => {
// this test will be skipped
it('[unstable] test', function () {
expect(this.stackOverflow.wiki).to.be('useful');
});
it('test two', function () {
expect(this.stackOverflow.answer).to.be('accepted');
});
});
});
What about running a single test?
Instead of using it.skip()
you could create a custom command that instead runs it.only()
and filter out tests that way. The world is your oyster.
How do I skip a test from somewhere else?
As long as you know what test or test suite you want to skip you could technically call the Cypress custom command (or just a generic helper method imported into your test file that has access to the Mocha module) from anywhere, so long as you have the right this
context (which is the desired Mocha context). The thing is, because Cypress queues commands internally (and Mocha runs test hooks in a specific order too), you will also need to ensure that you don't attempt to skip a test before the Mocha context is known (before your variable is actually assigned the context).
That and Cypress will complain if you attempt to run a custom command outside of a test -- well -- it will complain if you attempt to run any cy.command()
outside of a test, but that doesn't prevent you from using the Mocha module directly.
What this means is that skipping, failing, or selecting a test based on some logic is still inherently bound by the event order and events enqueued to be run by the Cypress
or cy
event emitters. You can deep dive into the Node event emitter API here but that stuff is a bit out of scope of this wiki so unfortunately, all you get is a link to the docs for this one.
I hope this has been useful to all you budding automation padowans out there - you got this!