Jasmine
Jasmine is a universal test runner, particularly popular with projects based on Angular framework. If your project already uses Jasmine to run its unit tests, you can use the same runner for your acceptance tests too.
In this article, you will learn:
- How to use Serenity/JS reporting services, including the Serenity BDD reporter, even if your test scenarios don't follow the Screenplay Pattern yet
- How to implement Jasmine test scenarios using reusable Serenity/JS Screenplay Pattern APIs
Examples and Project Templates​
If you'd like to dive straight into the code, Serenity/JS GitHub repository provides:
- Serenity/JS + Jasmine project templates, which are the easiest way to start with the framework,
- several reference implementations, demonstrating using Serenity/JS with Jasmine to write both REST API- and web-based acceptance tests
Using Serenity/JS reporting services​
To use Serenity/JS reporting services in a Jasmine project, you need to:
- attach the
@serenity-js/jasmine
test runner adapter to Jasmine - configure Serenity/JS to use the reporting services you want to use,
such as the
ConsoleReporter
orSerenityBDDReporter
Serenity/JS test runner adapters turn internal, test runner-specific events into Serenity/JS domain events that can contribute to test execution reports produced by Serenity/JS reporting services.
@serenity-js/jasmine
module provides a test runner adapter
you can attach to your Jasmine test runner.
Integration architecture described in this section applies to invoking
jasmine
command line interface directly, for example for domain-level,
REST/HTTP API-level, or web-based testing
using Playwright.
If you want your Jasmine scenarios to interact with web interfaces using Selenium Webdriver protocol, or connect them to a Selenium Grid, you should do so via Protractor or WebdriverIO instead.
Installing Serenity/JS test runner adapter​
Assuming you already have a Node.js project and Serenity/JS runtime dependencies set up, add the following Node modules:
To do that, run the following command in your terminal:
npm install --save-dev @serenity-js/core @serenity-js/jasmine jasmine
If you'd like to implement your test suite in TypeScript, also run:
- npm
- Yarn
npm install --save-dev typescript ts-node @types/jasmine @types/node
yarn add --dev typescript ts-node @types/jasmine @types/node
Configuring Serenity/JS​
If you intend to run your Jasmine scenarios using the Jasmine CLI,
the best way to configure Serenity/JS is to invoke the Serenity/JS configure
function
in a beforeAll
hook,
defined in a Jasmine helper file:
- TypeScript
- JavaScript
import 'jasmine'
import { configure } from '@serenity-js/core'
beforeAll(async () => {
// Configure Serenity/JS
configure({
crew: [
'@serenity-js/console-reporter',
'@serenity-js/serenity-bdd',
[ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
// ... any other reporting services
],
})
})
const { configure } = require('@serenity-js/core')
beforeAll(async () => {
// Configure Serenity/JS
configure({
crew: [
'@serenity-js/console-reporter',
'@serenity-js/serenity-bdd',
[ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
// ... any other reporting services
],
})
})
To learn more about installing and configuring Serenity/JS reporting services appropriate for your project, follow the Serenity/JS reporting guide.
Configuring Jasmine​
You can initialise Jasmine configuration file at spec/support/jasmine.json
by running the following command:
npx jasmine init
The resulting configuration file should look similar to the following:
{
"spec_dir": "spec",
"spec_files": [
"**/*[sS]pec.js"
],
"helpers": [
"helpers/**/*.js"
],
"stopSpecOnExpectationFailure": false,
"random": true
}
For TypeScript projects, modify spec/support/jasmine.json
as follows:
{
"spec_dir": "spec",
"spec_files": [
"**/*[sS]pec.ts"
],
"helpers": [
"helpers/**/*.ts"
],
"requires": [
"ts-node/register"
],
"stopSpecOnExpectationFailure": false,
"random": true
}
Defining Jasmine test scenarios​
When Serenity/JS reports on Jasmine test scenarios, it assumes you're following a common convention
where the outermost describe
block describes the name of the feature or component under test,
and any nested describe
blocks contribute to the name of the test scenario.
For example, Serenity/JS will report the below scenario as:
- feature:
Todo List App
- scenario:
when the user is a guest their list is empty
import 'jasmine'
describe('Todo List App', () => { // - feature or component name
describe('when the user is', () => { // - one or more nested `describe` blocks
describe('a guest', () => { // to group scenarios
describe('their list', () => { // by context in which they apply
it('is empty', async () => { // - expected behaviour of the feature or component
})
})
})
})
})
Using the same name for the outermost describe
block in several different spec files makes
Serenity BDD associate the different test scenarios with the same logical "feature" or "component"
and produce feature coverage metrics.
Attaching Serenity/JS test runner adapter​
To attach @serenity-js/jasmine
test runner adapter to Jasmine,
use the --reporter
option when invoking the test runner:
npx jasmine --reporter='@serenity-js/jasmine'
At the time of writing, Jasmine doesn't allow for reporters to be registered via the jasmine.json
configuration file.
Using Serenity/JS Screenplay Pattern APIs​
Serenity/JS actor model works great with Jasmine and Serenity/JS Screenplay Pattern APIs can help your team implement Jasmine test scenarios that are easy to read and understand.
The fastest way to get started with Serenity/JS and Jasmine is to use one of the Serenity/JS + Jasmine project templates. However, if you're adding Serenity/JS to an existing project or simply want to understand how the integration works, this guide is for you.
Configuring a cast of actors​
Serenity/JS Screenplay Pattern is an actor-centred model, so the first thing you need to do is to tell Serenity/JS what cast of actors you want to use.
If you're planning to run Jasmine scenarios using the Jasmine CLI directly,
you can configure the actors in a beforeAll
hook, for example:
import 'jasmine'
import { configure, Cast } from '@serenity-js/core'
import { BrowseTheWebWithPlaywright } from '@serenity-js/playwright'
import * as playwright from 'playwright'
let browser: playwright.Browser;
beforeAll(async () => {
// Launch the browser once before all the tests
// Serenity/JS will take care of managing Playwright browser context and browser tabs.
browser = await playwright.chromium.launch({
headless: true,
});
// Configure Serenity/JS
configure({
actors: Cast.where(actor =>
actor.whoCan(BrowseTheWebWithPlaywright.using(browser, {
baseURL: 'https://todo-app.serenity-js.org/',
}))
),
crew: [
'@serenity-js/console-reporter',
'@serenity-js/serenity-bdd',
[ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
// ... any other reporting services
],
})
})
afterAll(async () => {
// Close the browser after all the tests are finished
await browser?.close()
})
Consult the respective test runner instructions if you're invoking Jasmine indirectly, so via Protractor or WebdriverIO.
Referring to actors in test scenarios​
Serenity/JS actors are often used to represent user personas or important external systems
interacting with the system under test. In those scenarios, a common strategy is to give actors names indicating their persona,
and refer to them in your test scenarios using functions actorCalled
and actorInTheSpotlight
:
import 'jasmine'
import { Ensure, equals } from '@serenity-js/assertions'
import { actorCalled } from '@serenity-js/core'
import { Navigate, PageElements, By } from '@serenity-js/web'
describe('Todo List App', () => {
const displayedItems = () =>
PageElements.located(By.css('.todo-list li'))
.describedAs('displayed items')
describe('when the user is', () => {
describe('a guest', () => {
describe('their list', () => {
it('is empty', async () => {
await actorCalled('Alice').attemptsTo(
Navigate.to('https://todo-app.serenity-js.org/'),
Ensure.that(displayedItems().count(), equals(0))
)
})
})
})
})
})