Writing your first test with Bobcat
This guide uses bobcat-junit5
template to create tests.
Context
We will write a simple test that will check the following:
- Open the Wikipedia homepage.
- Search for ‘hello world’ in the search box.
- Open the first article
- Check that it’s the ‘“Hello, World!” program’ article.
Our first Page Objects
Always before jumping straight away into coding, it’s a good habit to stop and analyze what we are going to actually need to automate on our website. When it comes to modeling the necessary parts of the page (or multiple ones) as page objects, it’s a good idea to draw the relationship first. An important note here: page object doesn’t have to mean an actual page, it can also refer to a page section, single component etc.
To automate our test case we need to tackle the following elements on their related pages:
+ Wikipedia homepage
|--+ search box
|--+ input field
+ Wikipedia article
|--+ article heading
As you can see, we can organize the required elements in a tree structure.
It’s time to convert the above into code!
You might be familiar with Page Factory and @FindBy
annotations from vanilla Selenium - Bobcat uses them too.
Wikipedia homepage
package com.hello.world.wikipedia;
import com.cognifide.qa.bb.page.Page;
import com.cognifide.qa.bb.qualifier.PageObject;
import org.openqa.selenium.support.FindBy;
@PageObject
public class Homepage extends Page<Homepage>{
@FindBy(id = "p-search")
private SearchComponent searchComponent;
public SearchComponent getSearchComponent() {
return searchComponent;
}
}
Few elements probably require a bit of explanation:
@PageObject
annotation on the whole class informs Bobcat that this is a… page object :)! Thanks to this, Bobcat will create a proper page object structure and handle Page Factory initialization across the whole tree. Always remember about putting@PageObject
annotation on your page objects!Page.class
this class allows to use factory to create page with url required when we run test. It also gives us injected Webdriver. Guice, the dependency injection framework used in Bobcat, will create and provide a proper instance of WebDriver based on your configuration options. If you’re not interested in how it’s being done, you can simply stick with this information. If you’re more curious, you can checkWebDriverProvider
in Bobcat source :).-
@FindBy(id = "p-search") private SearchComponent searchComponent;
If you have worked with
@FindBy
earlier, you might noticed it’s not annotating a Selenium’sWebElement
but a different class. It’s one of the more powerful Bobcat features - the ability to nest page objects in other ones, i.e. you can put one page object inside another, using the@FindBy
annotation! Oh, it works with Lists as well.
Search component
package com.hello.world.wikipedia;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import com.cognifide.qa.bb.qualifier.PageObject;
@PageObject
public class SearchComponent {
@FindBy(css = "input[type=search]")
private WebElement searchField;
public void searchForQuery(String query) {
searchField.sendKeys(query);
searchField.submit();
}
}
This is a more regular Selenium example. One important thing to note here: thanks to the fact that the SearchComponent
has been embedded inside Homepage
with a @FindBy
annotation, all lookups done inside it will be done in the contexts of the locator from the @FindBy
!
What does it mean?
In our case, refering to the searchField
WebElement will trigger a search for element located by the #p-search input[type=search]
selector. As you can see, thanks to this, your selectors will be way more maintainable across more complicated page object trees.
Wikipedia article page
package com.hello.world.wikipedia;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import com.cognifide.qa.bb.qualifier.PageObject;
@PageObject
public class ArticlePage {
@FindBy(id = "firstHeading")
private WebElement heading;
public String getHeading() {
return heading.getText();
}
}
Nothing special in this page object :).
We have all the necessary pieces required to write the actual test!
Note: if you would like to read more about working with Page Objects in Bobcat, check out this page.
JUnit
When writing tests in JUnit5 Bobcat already creates Guice context what we must do is to add required modules and we do it with BobcatRunModule.class
Using it looks like this:
package com.hello.world.wikipedia;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import com.cognifide.qa.bb.page.BobcatPageFactory;
import com.google.inject.Inject;
import org.junit.jupiter.api.Test;
import com.cognifide.qa.bb.junit5.guice.Modules;
import com.cognifide.qa.bb.modules.BobcatRunModule;
@Modules(BobcatRunModule.class)
public class WikipediaTest {
private static final String SEARCH_QUERY = "hello world";
private static final String HEADING = "\"Hello, World!\" program";
@Inject
private BobcatPageFactory bobcatPageFactory;
@Inject
private ArticlePage articlePage;
@Test
public void wikipediaSearchTest() {
Homepage homePage = bobcatPageFactory.create("https://en.wikipedia.org", Homepage.class);
homePage.open().getSearchComponent().searchForQuery(SEARCH_QUERY);
assertThat(definitionPage.getHeading(), is(HEADING));
}
}
All it takes is adding the following annotations to your test class:
@Modules(BobcatRunModule.class)
The template has already created default.yaml
file that contains all required guice modules
Running tests
Now you’re ready to roll - trigger your test class from you IDE or execute the following command from the command line!
gradlew clean test
Summary
As you can see, the whole process of writing tests is pretty straightforward.