- Before you write a test
- Determine if end-to-end tests are needed
- Identify the DevOps stage
- Create a skeleton test
- Write the test
- De-duplicate your code
- Test setup using resources and page objects
- Write the page object
- Run the spec
- Preparing test for code review
- End-to-end test merge request template
Beginner’s guide to writing end-to-end tests
This tutorial walks you through the creation of end-to-end (e2e) tests forGitLab Community EditionandGitLab Enterprise Edition.
By the end of this tutorial, you can:
- Determine whether an end-to-end test is needed.
- Understand the directory structure within
qa/
. - Write a basic end-to-end test that validates login features.
- Develop any missingpage objectlibraries.
Before you write a test
Before you write tests, yourGitLab Development Kit (GDK)must be configured to run the specs. The end-to-end tests:
- Are contained within the
qa/
directory. - Should be independent andidempotent.
- Createresources(such as project, issue, user) on an ad-hoc basis.
- Test the UI and API interfaces, and use the API to efficiently set up the UI tests.
Determine if end-to-end tests are needed
Check the code coverage of a specific feature before writing end-to-end tests for theGitLabproject. Does sufficient test coverage exist at the unit, feature, or integration levels? If you answeredyes,then youdon’tneed an end-to-end test.
For information about the distribution of tests per level in GitLab, seeTesting Levels.
- See theHow to test at the correct level?section of theTesting levelsdocument.
- Review how often the feature changes. Stable features that don’t change very often might not be worth covering with end-to-end tests if they are already covered in lower level tests.
- Finally, discuss the proposed test with the developers involved in implementing the feature and the lower-level tests.
In this tutorial we’re writing a login end-to-end test, even though it has been sufficiently covered by lower-level testing, because it’s the first step for most end-to-end flows, and is easiest to understand.
Identify the DevOps stage
The GitLab QA end-to-end tests are organized by the differentstages in the DevOps lifecycle.Determine where the test should be placed bystage,determine which feature the test belongs to, and then place it in a subdirectory under the stage.
If the test is Enterprise Edition only, the test is created in thefeatures/ee
directory, but follow the same DevOps lifecycle format.
Create a skeleton test
In the first part of this tutorial we are testing login, which is owned by the Manage stage. Insideqa/specs/features/browser_ui/1_manage/login
,create a filebasic_login_spec.rb
.
The outercontext
block
See theRSpec.describe
outer block
context
was deprecatedin13.2
in adherence to RSpec 4.0 specifications. UseRSpec.describe
instead.The outerRSpec.describe
block
Specs have an outerRSpec.describe
indicating the DevOps stage.
# frozen_string_literal: true
moduleQA
RSpec.describe'Manage'do
end
end
Thedescribe
block
Inside of our outerRSpec.describe
,describe the feature to test. In this case,Login
.
# frozen_string_literal: true
moduleQA
RSpec.describe'Manage'do
describe'Login'do
end
end
end
Theproduct_group
metadata
Assignproduct_group
metadata and specify what product group this test belongs to. In this case,authentication_and_authorization
.
# frozen_string_literal: true
moduleQA
RSpec.describe'Manage'do
describe'Login',product_group::authenticationdo
end
end
end
Theit
blocks (examples)
Every test suite contains at least oneit
block (example). A good way to start writing end-to-end tests is to write test case descriptions asit
blocks:
moduleQA
RSpec.describe'Manage'do
describe'Login',product_group::authenticationdo
it'can login'do
end
it'can logout'do
end
end
end
end
Write the test
An important question is “What do we test?” and even more importantly, “How do we test?”
Begin by logging in.
# frozen_string_literal: true
moduleQA
RSpec.describe'Manage'do
describe'Login',product_group::authenticationdo
it'can login'do
Flow::Login.sign_in
end
it'can logout'do
Flow::Login.sign_in
end
end
end
end
Afterrunning the spec,our test should login and end; then we should answer the question “What do we test?”
# frozen_string_literal: true
moduleQA
RSpec.describe'Manage'do
describe'Login',product_group::authenticationdo
it'can login'do
Flow::Login.sign_in
Page::Main::Menu.performdo|menu|
expect(menu).tobe_signed_in
end
end
it'can logout'do
Flow::Login.sign_in
Page::Main::Menu.performdo|menu|
menu.sign_out
expect(menu).not_tobe_signed_in
end
end
end
end
end
What do we test?
- Can we sign in?
- Can we sign out?
How do we test?
- Check if the user avatar appears in the left sidebar.
- Check if the user avatardoes notappear in the left sidebar.
Behind the scenes,be_signed_in
is apredicate matcherthatimplements checking the user avatar.
De-duplicate your code
Refactor your test to use abefore
block for test setup, since it’s duplicating a call tosign_in
.
# frozen_string_literal: true
moduleQA
RSpec.describe'Manage'do
describe'Login',product_group::authenticationdo
beforedo
Flow::Login.sign_in
end
it'can login'do
Page::Main::Menu.performdo|menu|
expect(menu).tobe_signed_in
end
end
it'can logout'do
Page::Main::Menu.performdo|menu|
menu.sign_out
expect(menu).not_tobe_signed_in
end
end
end
end
end
Thebefore
block is essentially abefore(:each)
and is run before each example, ensuring we now sign in at the beginning of each test.
Test setup using resources and page objects
Next, let’s test something other than Login. Let’s test Issues, which are owned by the Plan stage and the Project Management Group, socreate a fileinqa/specs/features/browser_ui/2_plan/issue
calledissues_spec.rb
.
# frozen_string_literal: true
moduleQA
RSpec.describe'Plan'do
describe'Issues',product_group::project_managementdo
let(:issue){create(:issue)}
beforedo
Flow::Login.sign_in
issue.visit!
end
it'can close an issue'do
Page::Project::Issue::Show.performdo|show|
show.click_close_issue_button
expect(show).tobe_closed
end
end
end
end
end
Note the following important points:
- At the start of our example, we are at the
page/issue/show.rb
page. - Our test fabricates only what it needs, when it needs it.
- The issue is fabricated through the API to save time.
- GitLab prefers
let()
over instance variables. Seebest practices. -
be_closed
is not implemented inpage/project/issue/show.rb
yet, but is implemented in the next step.
The issue is fabricated as aResource,which is a GitLab entity you can create through the UI or API. Other examples include:
- AMerge Request.
- AUser.
- AProject.
- AGroup.
Write the page object
APage Objectis a class in our suite that represents a page within GitLab. TheLoginpage would be one example. Since our page object for theIssue Showpage already exists, add theclosed?
method.
modulePage::Project::Issue
classShow
view'app/views/projects/issues/show.html.haml'do
element'closed-status-box'
end
defclosed?
has_element?('closed-status-box')
end
end
end
Next, define the elementclosed-status-box
within your view, so your Page Object can see it.
-#=> app/views/projects/issues/show.html.haml
.issuable-status-box.status-box.status-box-issue-closed{...,data:{testid:'closed-status-box'}}
Run the spec
Before running the spec, make sure that:
- GDK is installed.
- GDK is running locally on port 3000.
- No additionalRSpec metadata tagshave been applied.
- Your working directory is
qa/
within your GDK GitLab installation. - Your GitLab instance-level settings are default. If you changed the default settings, some tests might have unexpected results.
- Because the GDK requires a password change on first login, you must include the GDK password for
root
user
To run the spec, run the following command:
GITLAB_PASSWORD=<GDK root password> bundleexecrspec <test_file>
Where<test_file>
is:
-
qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb
when running the Login example. -
qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
when running the Issue example.
Additional information on test execution and possible options are described in“QA framework README”
Preparing test for code review
Before submitting the test for code review, there are a few housecleaning tasks to do:
- Ensure that the test name follows the recommendednaming convention.
- Ensure that the spec islinked to a test case.
- Ensure that the spec has the correct
product_group
metadata. SeeProduct sections, stages, groups, and categoriesfor the comprehensive list of groups. - Ensure that the relevantRSpec metadataare added to the spec.
- Ensure the page object elements are named according to therecommended naming convention.
End-to-end test merge request template
When submitting a new end-to-end test, use the“New End to End Test”merge request description template for additional steps that are required prior a successful merge.