Robot Tutorial¶
This tutorial will step you through writing your first test, then enhancing that test with a custom keyword implemented as a page object. It is not a comprehensive tutorial on using Robot Framework. For Robot Framework documentation see the Robot Framework User Guide
It is assumed you’ve worked through the CumulusCI Get Started section at least up to the point where you’ve called cci project init
. It is also assumed that
you’ve read the Acceptance Testing with Robot Framework section of this document, which gives an overview of CumulusCI and Robot Framework integration.
Part 1: Folder Structure¶
We recommend that all Robot tests, keywords, data, and log and report
files live under a folder named robot
, at the root of your repository.
If you worked through the Get Started section, the following folders will have been created under
MyProject/robot/MyProject
:
doc
- a place to put documentation for your testsresources
- a place to put Robot libraries and keyword files that are unique to your projectresults
- a place for Robot to write its log and report filestests
- a place for all of your tests.
Part 2: Creating a custom object¶
For this tutorial we’re going to use a Custom Object named MyObject
(e.g. MyObject__c
). In addition, we need a Custom Tab that is
associated with that object.
If you want to run the tests and keywords in this tutorial verbatim, you will need to go to Setup and create the following:
A Custom Object with the name
MyObject
.A Custom Tab associated with this object.
Part 3: Creating and running your first Robot test¶
The first thing we want to do is create a test that verifies we can get to the listing page of the Custom Object. This will let us know that everything is configured properly.
Open up your favorite editor and create a file named MyObject.robot
in
the folder robot/MyProject/tests
. Copy and paste the following into
this file, and then save it.
*** Settings ***
Resource cumulusci/robotframework/Salesforce.robot
Library cumulusci.robotframework.PageObjects
Suite Setup Open test browser
Suite Teardown Delete records and close browser
*** Test Cases ***
Test the MyObject listing page
Go to page Listing MyObject__c
Current page should be Listing MyObject__c
Note
The above code uses Go to page
and Current page should be
, which
accept a page type (Listing
) and object name (MyObject__c
). Even
though we have yet to create that page object, the keywords will work by
using a generic implementation. Later, once we’ve created the page
object, the test will start using our implementation.
To run just this test, run the following command at the prompt:
$ cci task run robot -o suites robot/MyProject/tests/MyObject.robot --org dev
If everything is set up correctly, you should see the output that looks similar to this:
$ cci task run robot -o suites robot/MyProject/tests/MyObject.robot --org dev
2019-05-21 17:29:25: Getting scratch org info from Salesforce DX
2019-05-21 17:29:29: Beginning task: Robot
2019-05-21 17:29:29: As user: test-wftmq9afc3ud@example.com
2019-05-21 17:29:29: In org: 00Df0000003cuDx
2019-05-21 17:29:29:
==============================================================================
MyObject
==============================================================================
Test the MyObject listing page | PASS |
------------------------------------------------------------------------------
MyObject | PASS |
1 critical test, 1 passed, 0 failed
1 test total, 1 passed, 0 failed
==============================================================================
Output: /Users/boakley/dev/MyProject/robot/MyProject/results/output.xml
Log: /Users/boakley/dev/MyProject/robot/MyProject/results/log.html
Report: /Users/boakley/dev/MyProject/robot/MyProject/results/report.html
Part 4: Creating a page object¶
Most projects are going to need to write custom keywords that are unique to that project. For example, NPSP has a keyword for filling in a batch gift entry form, EDA has a keyword with some custom logic for validating and affiliated contact, and so on.
The best way to create and organize these keywords is to place them in
page object libraries. These libraries contain normal Python classes and
methods which have been decorated with the pageobjects
decorator
provided by CumulusCI. By using page objects, you can write keywords
that are unique to a given page, making them easier to find and easier
to manage.
Defining the class¶
CumulusCI provides the base classes that are a good starting point for
your page object (see Page Object Base Classes). In this case we’re writing a keyword that works on the
listing page, so we want our class to inherit from the ListingPage
class.
Note
Our class also needs to use the pageobject
decorator, so we must
import that along with the ListingPage
class.
To get started, create a new file named MyObjectPages.py
in the folder
robot/MyProject/resources
. At the top of the new keyword file, add the
following import statement:
from cumulusci.robotframework.pageobjects import pageobject, ListingPage
Next we can create the class definition by adding the following two lines:
@pageobject(page_type="Listing", object_name="MyObject__c")
class MyObjectListingPage(ListingPage):
The first line registers this class as a page object for a listing page
for the object MyObject__c
. The second line begins the class
definition.
Creating the keyword¶
At this point, all we need to do to create the keyword is to create a method on this object. The method name should be all lowercase, with underscores instead of spaces. When called from a Robot test, the case is ignored and all spaces are converted to underscores.
In this case we want to create a method named
click_on_the_row_with_name
. All we want it to do is to find a link
with the given name, click on the link, and then wait for the new page
to load. To make the code more bulletproof, it will use a keyword from
SeleniumLibrary
to wait until the page contains the link before
clicking on it. While probably not strictly necessary on this page,
waiting for elements before interacting with them is a good habit to get
into.
Add the following under the class definition:
def click_on_the_row_with_name(self, name):
xpath='xpath://a[@title="{}"]'.format(name)
self.selenium.wait_until_page_contains_element(xpath)
self.selenium.click_link(xpath)
self.salesforce.wait_until_loading_is_complete()
Notice that the above code is able to use the built-in properties
self.selenium
and self.salesforce
to directly call keywords in the
SeleniumLibrary
and Salesforce
keyword libraries.
Putting it all together¶
After adding all of the above code, our file should now look like this:
from cumulusci.robotframework.pageobjects import pageobject, ListingPage
@pageobject(page_type="Listing", object_name="MyObject__c")
class MyObjectListingPage(ListingPage):
def click_on_the_row_with_name(self, name):
xpath='xpath://a[@title="{}"]'.format(name)
self.selenium.wait_until_page_contains_element(xpath)
self.selenium.click_link(xpath)
self.salesforce.wait_until_loading_is_complete()
We now need to import this page object into our tests. In the first
iteration of the test, we imported
cumulusci.robotframework.PageObjects
, which provided our test with
keywords such as Go to page
and Current page should be
. In addition
to being the source of these keywords, it is also the way to import page
object files into a test case.
To import a file with one or more page objects you need to supply the
path to the page object file as an argument when importing
PageObjects
. The easiest way is to use Robot’s continuation
characters ...
on a separate line.
Modify the import statements at the top of MyObject.robot
to look like
the following:
*** Settings ***
Resource cumulusci/robotframework/Salesforce.robot
Library cumulusci.robotframework.PageObjects
... robot/MyProject/resources/MyObjectPages.py
This will import the page object definitions into the test case, but the
keywords won’t be available until the page object is loaded. Page
objects are loaded automatically when you call Go to page
, or you can
explicitly load them with Load page object
. In both cases, the first
argument is the page type (eg: [Listing]{.title-ref},
[Home]{.title-ref}, etc) and the second argument is the object name (eg:
MyObject__c
).
Our test is already using Go to page
, so our keyword should already be
available to us once we’ve gone to that page.
Part 5: Adding test data¶
We want to be able to test that when we click on one of our custom objects on the listing page that it will take us to the detail page for that object. To do that, our test needs some test data. While that can be very complicated in a real-world scenario, for simple tests we can use the Salesforce API to create test data when the suite first starts up.
To create the data when the suite starts, we can add a Suite Setup
in
the settings section of the test. This takes as an argument the name of
a keyword. In our case we’re going to create a custom keyword right in
the test to add some test data for us.
It is not necessary to do it in a setup. It could be a step in an
individual test case, for example. However, putting it in the
Suite Setup
guarantees it will run before any tests in the same file
are run.
Open up MyObject.robot
and add the following just before
*** Test Cases ***
:
*** Keywords ***
Create test data
[Documentation]
... Creates a MyObject record named "Leeroy Jenkins"
... if one doesn't exist
## Check to see if the record is already in the database,
## and return if it already exists
${status} ${result}= Run keyword and ignore error Salesforce get MyObject__c Name=Leeroy Jenkins
Return from keyword if '${status}'=='PASS'
## The record didn't exist, so create it
Log creating MyObject object with name 'Leeroy Jenkins' DEBUG
Salesforce Insert MyObject__c Name=Leeroy Jenkins
We also need to modify our Suite Setup
to call this keyword in
addition to calling the Open Test Browser
keyword. Since Suite Setup
only accepts a single keyword, we can use the built-in keyword
Run keywords
to run more than one keyword in the setup.
Change the suite setup to look like the following, again using Robot’s continuation characters to spread the code across multiple rows for readability.
Note
It is critical that you use all caps for AND
, as that’s the way Robot
knows where one keyword ends and the next begins.
Suite Setup Run keywords
... Create test data
... AND Open test browser
Notice that our Suite Teardown
calls
Delete records and close browser
. The records
in that keyword
refers to any data records created by Salesforce Insert
. This makes it
possible to both create and later clean up temporary data used for a
test.
It is important to note that the suite teardown isn’t guaranteed to run
if you forcibly kill a running Robot test. For that reason, we added a
step in Create test data
to check for an existing record before adding
it. If a previous test was interrupted and the record already exists,
there’s no reason to create a new record.
Part 6: Using the new keyword¶
We are now ready to modify our test to use our new keyword, since we now have some test data in our database, and the keyword definition in our page object file.
Once again, edit MyObject.robot
to add the following two statements at
the end of our test:
Click on the row with name Leeroy Jenkins
Current page should be Detail MyObject__c
The complete test should now look like this:
*** Settings ***
Resource cumulusci/robotframework/Salesforce.robot
Library cumulusci.robotframework.PageObjects
... robot/MyProject/resources/MyObjectPages.py
Suite Setup Run keywords
... Create test data
... AND Open test browser
Suite Teardown Delete records and close browser
*** Keywords ***
Create test data
[Documentation] Creates a MyObject record named "Leeroy Jenkins" if one doesn't exist
## Check to see if the record is already in the database,
## and do nothing if it already exists
${status} ${result}= Run keyword and ignore error Salesforce get MyObject__c Name=Leeroy Jenkins
Return from keyword if '${status}'=='PASS'
## The record didn't exist, so create it
Log creating MyObject object with name 'Leeroy Jenkins' DEBUG
Salesforce Insert MyObject__c Name=Leeroy Jenkins
*** Test Cases ***
Test the MyObject listing page
Go to page Listing MyObject__c
Current page should be Listing MyObject__c
Click on the row with name Leeroy Jenkins
Current page should be Detail MyObject__c
With everything in place, we should be able to run the test using the same command as before:
$ cci task run robot -o suites robot/MyProject/tests/MyObject.robot --org dev
2019-05-21 22:02:27: Getting scratch org info from Salesforce DX
2019-05-21 22:02:31: Beginning task: Robot
2019-05-21 22:02:31: As user: test-wftmq9afc3ud@example.com
2019-05-21 22:02:31: In org: 00Df0000003cuDx
2019-05-21 22:02:31:
==============================================================================
MyObject
==============================================================================
Test the MyObject listing page | PASS |
------------------------------------------------------------------------------
MyObject | PASS |
1 critical test, 1 passed, 0 failed
1 test total, 1 passed, 0 failed
==============================================================================
Output: /Users/boakley/dev/MyProject/robot/MyProject/results/output.xml
Log: /Users/boakley/dev/MyProject/robot/MyProject/results/log.html
Report: /Users/boakley/dev/MyProject/robot/MyProject/results/report.html