Selenium Tests
Good & Evil
🖊️ Work in progress
Topics
- Verständlichkeit der Tests
- Testablauf
- Im Fehlerfall
- Test Aufbau
- Hands On
- Good to Know
Verständlichkeit
Des Tests
Testschritte benennen
resellerApplication.action().createNewCartInPortal("New Cart");
CartRow newCart = portal.getCartTable().getCartByName("New Cart");
newCart.action().addProduct(appleiPadAir);
newCart.action().shareFirstLineItem();
newCart.action().addNegotiationPartner(distributorUser.getUserName());
SearchPage<?> searchPage = openPage().globalSearchNegotiationPage();
SellerResponsePage<?> sellerResponsePage =
searchPage.openFirstSellerResponseFromCompany(getCompany());
sellerResponsePage.getFirstRow().setPrice("1.00");
CartColumnTableRow supplierCard =
newCart.getCartColumn().getCartTable().getFirstSupplierCard();
supplierCard = selectNegotiationPosition(1, newCart, supplierCard);
priceRequestTest()
Testschritte benennen
// #1 (Buyer) create new cart and open it
resellerApplication.action().createNewCartInPortal("New Cart");
// #2 (Buyer) add Product and start negotiation with distri Also
CartRow newCart = portal.getCartTable().getCartByName("New Cart");
newCart.action().addProduct(appleiPadAir);
newCart.action().shareFirstLineItem();
newCart.action().addNegotiationPartner(distributorUser.getUserName());
// #3 (distri) set a price
SearchPage<?> searchPage = openPage().globalSearchNegotiationPage();
SellerResponsePage<?> sellerResponsePage =
searchPage.openFirstSellerResponseFromCompany(getCompany());
sellerResponsePage.getFirstRow().setPrice("1.00");
// #4 Select negotiation price
CartColumnTableRow supplierCard =
newCart.getCartColumn().getCartTable().getFirstSupplierCard();
supplierCard = selectNegotiationPosition(1, newCart, supplierCard);
priceRequestTest()
Componenten statt statische IDs
waitForElementVisibile(getDriver(), By.id(RedgiantNotificationWindow.ID));
Componenten statt statische IDs
waitForElementVisibile(getDriver(), By.id(RedgiantNotificationWindow.ID));
// vs
assertThat(window, is(visible));
Componenten statt getWebelement
window.getWebElement().findElement(
By.id("-RedgiantNotificationWindow-TextField")).clear();
showErrorTest()
Componenten statt getWebelement
window.getWebElement().findElement(
By.id("-RedgiantNotificationWindow-TextField")).clear();
// vs
notificationWindow().getWarningTextField().clear();
showErrorTest()
Componenten statt getWebelement
actions().dragAndDrop(
card.getDraggableComponent().getWebElement(),
((MultiCartPortalDealColumn) sendDealColumn).getWebElement()).
build().perform();
dragDropCartCardTest()
Componenten statt getWebelement
actions().dragAndDrop(
card.getDraggableComponent().getWebElement(),
((MultiCartPortalDealColumn) sendDealColumn).getWebElement()).
build().perform();
// vs
// HasDragHandler // HasDropHandler
card.dragAndDropTo(sendDealColumn);
dragDropCartCardTest()
Konstruktion von Primitive Komponenten
assertThat(new Label(getDriver(), By.id(ID_ERROR_LABEL)).getText(),
equalToIgnoringCase(CHECK_ERROR_LABEL_PERMISSION));
showErrorTest()
Konstruktion von Primitive Komponenten
assertThat(new Label(getDriver(), By.id(ID_ERROR_LABEL)).getText(),
equalToIgnoringCase(CHECK_ERROR_LABEL_PERMISSION));
// vs
assertThat(errorLabel, hasText("not enough acces rights"));
showErrorTest()
Adressierung per ID
dialog.getSalutation().selectComboItem(2);
dialog.getContactTitle().selectComboItem(5);
showErrorTest()
Adressierung per ID
dialog.getSalutation().selectComboItem(2);
dialog.getContactTitle().selectComboItem(5);
//vs
dialog.getSalutation().selectComboItem("Mrs");
dialog.getContactTitle().selectComboItem("Prof.");
showErrorTest()
Adressierung per ID
dialog.getSalutation().selectComboItem(2);
dialog.getContactTitle().selectComboItem(5);
//vs
dialog.getSalutation().selectComboItem("Mrs");
dialog.getContactTitle().selectComboItem("Prof.");
//vs
dialog.getSalutation().selectMr();
dialog.getContactTitle().selectDr();
showErrorTest()
Produkt abhängiges in TestProduct kapseln
DLinkDes1005DSwitch.getMarketingText()
productPreviewTest()
Component basierte Matcher verwenden
assertThat(quotePage.getLineItemTable().size(), is(1));
// vs
assertThat(quotePage.getLineItemTable(), hasSize(1));
quotePageTest()
Component basierte Matcher verwenden
assertThat(quotePage.getLineItemTable().size(), is(1));
// vs
assertThat(quotePage.getLineItemTable(), hasSize(1));
quotePageTest()
- containsText("text")
- hasCaption("text")
- hasDimension(100, 200);
- hasError()
- hasPartnerStatusTag()
- hasSize(200)
- hasTag("tag")
- hasText("text")
- hasTrackingEvents()
- isActive()
- isCollapsed()
- isEmpty()
- isEnabled()
- isFolded()
- isInFocus()
- isMarked()
- isNotInFocus()
- isNotVisible()
- isSelected()
- isVisible()
Component basierte Matcher verwenden
assertThat(contactPage.getLineItemTable().visible(), is(true));
// vs
assertThat(contactPage.getLineItemTable(), isVisible(1));
contactPageTest()
Component basierte Matcher verwenden
assertThat(contactPage.getLineItemTable().visible(), is(true));
// vs
assertThat(contactPage.getLineItemTable(), is(visible()));
contactPageTest()
Expected true but was false.
Expected component is visible but component (id:Application.Seach.Button) was not visible.
Component basierte Matcher verwenden
assertThat(sendButton, is(not(visible())));
// not the same !
assertThat(sendButton, is(notVisible())));
sendMailTest()
⚠️
Keine langen Imports
de.itscope.redgiant.testbench.element.application.frontend.
LabsFrontendApplication.loginWithCookieAndOpenPage(
getDriver(), email, password, getTestCaseName());
loginWithCookiesTest()
Keine langen Imports
de.itscope.redgiant.testbench.element.application.frontend.
LabsFrontendApplication.loginWithCookieAndOpenPage(
getDriver(), email, password, getTestCaseName());
\\ vs
loginWithCookieAndOpenPage(getDriver(), email, password, getTestCaseName());
loginWithCookiesTest()
Keine lange Selektoren
MenuItem printPDFMenuItem = cart.getSendDealColumn()
.getDealTable()
.getFirstRow()
.getHeader()
.getRowActions()
.getMenuItem("Download")
.openSubMenu()
.getMenuItem("Download PDF");
printPdfTest()
Keine lange Selektoren
MenuItem printPDFMenuItem = cart.getSendDealColumn()
.getDealTable()
.getFirstRow()
.getHeader()
.getRowActions()
.getMenuItem("Download")
.openSubMenu()
.getMenuItem("Download PDF");
\\ vs
MenuItem printPDFMenuItem = firstDealcard.getDownloadPDFMenu();
printPdfTest()
Keine uncaught Exceptions
return getRows().get(2);
customerTableTest()
⚡ NullpointerException
⚡ IndexOutOfBoundsException
Test Aufbau
Gerneric Typen verwenden
public class QuoteGuestPageTable extends CustomLayoutTable {
quoteGuestPageTableTest()
Gerneric Typen verwenden
public class QuoteGuestPageTable extends CustomLayoutTable {
// vs
public class QuoteGuestPageTable extends
CustomLayoutTable<QuoteGuestPageTable.QuoteGuestPageRow> {
quoteGuestPageTableTest()
Konkrete Widgets nutzen
personTabShee.getTab("Alle")
createCustomContactTest()
Konkrete Widgets nutzen
personTabShee.getTab("Alle")
\\ vs
personTabShee.getTabAll();
createCustomContactTest()
Konkrete Widgets nutzen
personTabShee.getTab("Alle")
\\ vs
personTabShee.getTabAll();
createCustomContactTest()
public static class ContactTabSheet extends TabSheet<TabSheet.Tab> {
private ContactTabSheet(WebDriver driver, Component parent) { ... }
public TabSheet.Tab getTabAll() {
return getTab("ALLE");
}
}
Keine IDs konkatenieren
static String SUFFIX_EMPLOYEE_SINGLE = "-EmployeeSingleView";
static String SUFFIX_EMPLOYEE_LAYOUT = "_employeeLayout";
static String SUFFIX_HEADING = SUFFIX_EMPLOYEE_LAYOUT + "_headingLayout";
static String SUFFIX_AVATAR = SUFFIX_HEADING + "_image";
static String SUFFIX_CONTACT = SUFFIX_HEADING + "-ContactEditView";
static String SUFFIX_TABSHEET = SUFFIX_EMPLOYEE_LAYOUT + "_verticalTabSheet";
static String SUFFIX_ACTIVITY = SUFFIX_EMPLOYEE_LAYOUT + "_activityIndexBar";
static String SUFFIX_PROFILE = "-ProfileLayout";
static String SUFFIX_SETTINGS = "-SettingsLayout";
EmployeeSingleView
Keine IDs konkatenieren
static class EmployeeLayout extends Component {
public EmployeeLayout(WebDriver driver, Component parent) {...}
public Label getActivity() {
return new Label(getDriver(), By.id(getId() + "_activityIndexBar"));
}
public HeadingLayout getHeadingLayout() {
return new HeadingLayout(getDriver(), this);
}
static class HeadingLayout extends Component {
public Image getAvatar() {
return new Image(getDriver(), By.id(getId() + "_image"));
}
}
EmployeeSingleView
Referenzierung über Text gegenüber ID bevorzugen
public ComboBox getDepartment() {
return new ComboBox(driver, By.id(getId() + "-ComboBox3"));
}
public TextField getEmail() {
return new TextField(driver, By.id(getId() + "-TextField4"));
}
public TextField getFax() {
return new TextField(driver, By.id(getId() + "-TextField8"));
}
public TextField getMobile() {
return new TextField(driver, By.id(getId() + "-TextField7"));
}
public TextField getPhone() {
return new TextField(driver, By.id(getId() + "-TextField6"));
}
EmployeeSingleView
Referenzierung über Text gegenüber ID bevorzugen
public ComboBox getDepartment() {
return getFormLayoutRow(ComboBox::new, "Abteilung").getContent();
}
public TextField getEmail() {
return getFormLayoutRow(TextField::new, "E-Mail").getContent();
}
public TextField getFax() {
return getFormLayoutRow(TextField::new, "Fax").getContent();
}
public TextField getMobile() {
return getFormLayoutRow(TextField::new, "Telefon (Mobil)").getContent();
}
public TextField getPhone() {
return getFormLayoutRow(TextField::new, "Telefon (Büro)").getContent();
}
EmployeeSingleView
Statische Klassen
verwenden
public class QuotePageRow extends CustomLayoutTable.Row {\\...
priceRequestTest()
Statische Klassen
verwenden
public class QuotePageRow extends CustomLayoutTable.Row {\\...
\\ vs
public staic class QuotePageRow extends CustomLayoutTable.Row {\\...
priceRequestTest()
Asserts im Test, nicht bei getComponent()
public SwitchButton getToogleVisibility() {
assertThat(getSwitchButton(), is(visible()));
return getSwitchButton().getText();
}
priceRequestTest()
Stateless Mocks
Lazy Mocks
private Button buttonCancel;
public Button getButtonCancel() {
if (buttonCancel == null) {
buttonCancel = new Button(driver, By.id(getId() + "-Button"));
}
return buttonCancel;
}
\\ vs
public Button getButtonCancel() {
return new Button(driver, By.id(getId() + "-Button"));
}
EmailChangeDialog
👨🏼💻
Keine Sleeps im Test
ToolTip toolTip = new ToolTip(driver,
By.xpath("//div[contains(@class, 'v-tooltip')]"));
try {
Thread.sleep(1000L);
} catch (InterruptedException ignored) {
}
return toolTip;
priceRequestTest()
Keine Bedingungen im Test
if (datasheetSelection.isSelected()) {
datasheetSelection.click();
}
showDatasheetTest()
Good To Know
CLT immer mit custom row Style bauen
public static class CartTable extends CustomLayoutTable<CartRow> {
@Override
protected String getRowCssClass() {
return super.getRowCssClass() + "-cartcolumn";
}
CartTable
Default Setup mit RedgiantFrontendTest
private LabsFrontendApplication application;
public Employee_ManageCompanyRights() {
super();
}
@Override
@Before
public void setUp() throws Exception {
super.initializeDefaultDriver();
String email = getDriver().getCompany().getUserEmail();
String password = getDriver().getCompany().getUserPassword();
application = LabsFrontendApplication.loginWithCookieAndOpenPage(
getDriver(), email, password, getTestCaseName());
}
Employee_ManageCompanyRights
Default Setup mit RedgiantFrontendTest
private LabsFrontendApplication application;
public Employee_ManageCompanyRights() {
super();
}
@Override
@Before
public void setUp() throws Exception {
super.initializeDefaultDriver();
String email = getDriver().getCompany().getUserEmail();
String password = getDriver().getCompany().getUserPassword();
application = LabsFrontendApplication.loginWithCookieAndOpenPage(
getDriver(), email, password, getTestCaseName());
}
Employee_ManageCompanyRights
public class Employee_ManageCompanyRights extends RedgiantFrontendTest
vs
URL auf Erreichbarkeit in JUnit testen
@WikiTest(property = "exportsAndAPI.authentication")
@Test
public void testGetWikiPageApiCredentials() throws Exception {
assertThatUrlIsAReachableWikiTopic(
links.getExportsAndAPI().getAuthentication(),
"Authentifizierung über API Zugangsdaten");
}
WikiLinksAvailabilityTest
URL auf Erreichbarkeit in JUnit testen
@WikiTest(property = "exportsAndAPI.authentication")
@Test
public void testGetWikiPageApiCredentials() throws Exception {
assertThatUrlIsAReachableWikiTopic(
links.getExportsAndAPI().getAuthentication(),
"Authentifizierung über API Zugangsdaten");
}
WikiLinksAvailabilityTest
@Test
public void ensureAllWikiLinksAreCheckedInTest() {...}
WikiLinksTestCoverageCheck
Emails im Unit Test
@Test
public void testInviteITscope() {
EmailDef emailDef = new EmailDefMock(EmailTemplate.Invitation.Itscope,
EmailTemplate.Base.FooterLong);
final TeaserDTO teaserDTO = new TeaserDTOMock(Locale.GERMANY);
InviteDTO inviteDTO = new InviteDTOMock( teaserDTO);
inviteDTO.setRegisterLink("https://www.itscope.com/red/register/abc132");
EmailConfiguration mailConfig = new EmailConfigurationMock(dto, GERMANY);
String mail = mailConfig.createEmail(emailConfig).getBody();
assertThat(mail, containsString("über 3 Mio. ITK-Artikeln"));
assertThat(mail, containsString(
"<a href=\"https://www.itscope.com/red/register/abc132\"
class=button>Jetzt kostenlos registrieren »</a>"));
assertThatNoEmtpyTemplateVarsIn(mail);
Approvals.verifyHtml(mail);
}
WikiLinksAvailabilityTest
Emails im Unit Test
public class TeaserDTOMock extends TeaserDTO {
public TeaserDTOMock(Locale locale) {
super(locale);
// Redgiant links
setProductBoardUrl("https://www.itscope.com/red/app#feedback/");
// Wiki links
setUrlSupport("https://support.itscope.com/hc/de/");
setUrlSupportSearch("https://support.itscope.com/hc/de/articles/206134861");
setUrlSupportAddSupplier("https://support.itscope.com/hc/de/articles/360000028039");
setUrlSupportOptimization("https://support.itscope.com/hc/de/articles/206174701");
setUrlSupportQueryDistributors("https://support.itscope.com/hc/de/articles/360000028039");
setUrlSupportOnlineQuotes("https://support.itscope.com/hc/de/articles/206033202");
setUrlSupportOnlineQuotes("https://support.itscope.com/hc/de/articles/206033202");
setUrlSupportPriceCalculation("https://support.itscope.com/hc/de/articles/206129589");
setUrlSupportCollections("https://support.itscope.com/hc/de/articles/206031722");
setUrlSupportAboutYourCompany("https://support.itscope.com/hc/de/articles/209561185");
}
}
WikiLinksAvailabilityTest
Golden Master Testing
mit Approvals
@Test
public void testInviteITscope() {
// ...
String mail = mailConfig.createEmail(emailConfig).getBody();
// ...
Approvals.verifyHtml(mail);
}
WikiLinksAvailabilityTest
Golden Master Testing
mit Approvals
@Test
public void testInviteITscope() {
// ...
String mail = mailConfig.createEmail(emailConfig).getBody();
// ...
Approvals.verifyHtml(mail);
}
WikiLinksAvailabilityTest
🗎 InviteMailTest.testActivateUser.approved.html
🗎 InviteMailTest.testActivateUser.received.html
👨🏼💻
Componenten in ID-Generierung beeinflussen
@IgnoreInDebugId
public class PersonTableAndBarView extends
VerticalLayout implements ICombinedPersonTableAndSearchBarView {
//...
}
PersonTableAndBarView
@DebugId("CustomLayoutTable")
public class RedgiantCustomLayoutTable<Slot extends Enum<?>>
extends CustomLayoutTable {
// ...
}
RedgiantCustomLayoutTable
Basis Setup mit RedgiantFrontendTest
@RunWith(DelegatingRunnerFactory.class)
public class RedgiantFrontendTest extends RedgiantTest {
protected LabsFrontendApplication application;
@Override
public void setUp() throws Exception {
// loginWithCookieAndOpenPage()
}
}
RedgiantFrontendTest
Basis Setup mit RedgiantFrontendTest
@Override
@Before
public void setUp() throws Exception {
super.initializeDefaultDriver(
TestSetups.defaultCompany(
createRandomCompanyName(this)).
withTestUserPermission(TestSetup.TestUserPermissionDef.ORDER).
build());
String email = getDriver().getCompany().getUserEmail();
String password = getDriver().getCompany().getUserPassword();
application = login(getDriver(), email, password, getTestCaseName());
searchPage = application.openPage().companySearchPage();
}
Organisation_ContactNavigation
Basis Setup mit RedgiantFrontendTest
@Override
@Before
public void setUp() throws Exception {
super.initializeDefaultDriver(
TestSetups.defaultCompany(
createRandomCompanyName(this)).
withTestUserPermission(TestSetup.TestUserPermissionDef.ORDER).
build());
String email = getDriver().getCompany().getUserEmail();
String password = getDriver().getCompany().getUserPassword();
application = login(getDriver(), email, password, getTestCaseName());
searchPage = application.openPage().companySearchPage();
}
Organisation_ContactNavigation
Basis Setup mit RedgiantFrontendTest
@Override
@Before
public void setUp() throws Exception {
super.initializeDefaultDriver(tesSetup().defaultCompany().
withTestUserPermission(TestUserPermissionDef.ORDER));
super.setUp();
searchPage = application.openPage().companySearchPage();
}
Organisation_ContactNavigation
Selenium Tests - Good & Evil
By Tobse Fritz
Selenium Tests - Good & Evil
Was wir an unseren Testworkflow gelern haben
- 667