SlideShare a Scribd company logo
1 of 107
Download to read offline
KISS-Driven Test Automation
по лезвию бритвы Оккама
Alexei VinogradovMaster class
KISS-Driven Test Automation
по лезу бритви Оккама
Олекciй ВiноградовМайстер-клас
Олекciй Вiноградов

IT-Kонсультант



тестування, управління тестуванням,
автоматизація в тестуванні, коучинг
В області IT c 1998, доповідач c 2014 року
Студент-стажер -> Програмiст -> Тестувальник ->…
http://radio-qa.com
https://software-testers.herokuapp.com
SoftwareTesters Slack-Chat
Мой личный опыт 

отличается от вашего
Для всех правил из этого
доклада есть исключения
Личный опыт любого
докладчика

отличается от вашего
Для всех правил из всех
докладов есть исключения
The best since the 14. century
"Бритва Оккама"
Уильям Оккам

1285 - 1349, Англия
Не следует привлекать

новые сущности без крайней

на то необходимости
«Что может быть сделано на основе меньшего числа
[предположений], не следует делать, исходя из большего»
Не аксиома, а
предложение
X <- A, B, C
X <- A, B, C, D
Бритва Оккама
Лучший код
- это ненаписанный код
KISS-Driven Test Automation

(KDTA)
ПРОСТОТА
это круто!!!
1. Начинай с KISS
2. Продолжай KISS
3. Отказывайся от KISS только по хорошим причинам
Три принципа KDTA
KISS over SOLID
KISS over Design Patterns
KISS over DRY
Make KISS
not over-engineering
Писать простой код сложно
Сложнее всего обходится без
кода совсем
Слайды!
Примеры
Примеры: UI Test Automation
Не надо писать фреймворки*,
надо писать тесты
*на работе
http://selenide.org
Selenium inside
UI Testing Framework
https://github.com/vinogradoff/selenide101/tree/
master/src/test/java/de/vinogradoff/kiss_examples
https://github.com/vinogradoff/selenide101/
Код в GitHub
Типизированные UI элементы
Button
Link
Image
Checkbox
PageObject
public class OtherPageObject{
Text description=new Text(By.cssSelector("#text"));
Button submit=new Button(By.cssSelector("#button"));
Label productName=new Label(By.cssSelector("#label"));
Link followMe=new Link(By.cssSelector("#link"));
TextField passwordField=new TextField(By.cssSelector("#textfield"));
public void useElements(){
description.getText();
submit.click();
productName.shouldBe(visible);
followMe.click();
passwordField.setValue("password");
}
}
PageObject
public class OtherPageObject{
Text description=new Text(By.cssSelector("#text"));
Button submit=new Button(By.cssSelector("#button"));
Label productName=new Label(By.cssSelector("#label"));
Link followMe=new Link(By.cssSelector("#link"));
TextField passwordField=new TextField(By.cssSelector("#textfield"));
public void useElements(){
description.getText();
submit.click();
productName.shouldBe(visible);
followMe.click();
passwordField.setValue("password");
}
}
KISS PageObject
public class KISSPageObject {
SelenideElement description=$("#text"),
submitBtn=$("#button"),
productName=$("#label"),
followMe=$("#link"),
passwordField=$("#textfield");
public void useElements(){
description.getText();
submitBtn.click();
productName.shouldBe(visible);
followMe.click();
passwordField.setValue("password");
}
}
getter/setter
PageObject
public class OtherPageObject {
private SelenideElement productName=$("#label");
public SelenideElement getProductName() {
return productName;
}
private SelenideElement submitBtn=$("#button");
private SelenideElement followMe=$("#link");
public void useElements(){
submitBtn.click();
followMe.click();
}
}
Test
public class OtherTest {
@Test
public void testProductName(){
new OtherPageObject().getProductName().shouldBe(visible);
}
}
PageObject
public class OtherPageObject {
private SelenideElement productName=$("#label");
public SelenideElement getProductName() {
return productName;
}
private SelenideElement submitBtn=$("#button");
private SelenideElement followMe=$("#link");
public void useElements(){
submitBtn.click();
followMe.click();
}
}
KISS PageObject
public class KISSPageObject {
public SelenideElement productName=$("#label");
private SelenideElement submitBtn=$("#button");
private SelenideElement followMe=$("#link");
public void useElements(){
submitBtn.click();
followMe.click();
}
}
Test
public class KISSTest {
@Test
public void testProductName(){
new OtherPageObject().productName.shouldBe(visible);
}
}
Lombok PageObject
public class LombokPageObject {
@Getter private SelenideElement productName=$("#label"); //Lombok 

private SelenideElement submitBtn=$("#button");
private SelenideElement followMe=$("#link");
public void useElements(){
submitBtn.click();
followMe.click();
}
}
Lombok PageObject
public class LombokPageObject {
@Getter private SelenideElement productName=$("#label"); //Lombok 

private SelenideElement submitBtn=$("#button");
private SelenideElement followMe=$("#link");
public void useElements(){
submitBtn.click();
followMe.click();
}
}
@FindBy & PageFactory
PageObject
public class OtherPageObject {
@FindBy(css = "#text")
SelenideElement description;
@FindBy(css = "#button")
WebElement submit;
@FindBy(css = "#label")
SelenideElement productName;
@FindBy(css = "#link")
WebElement followMe;
public void useElements(){
description.getText();
submit.click();
productName.shouldBe(visible);
followMe.click();
}
}
PageObject
public class OtherPageObject {
public OtherPageObject(){
PageFactory.initElements(WebDriverRunner.getWebDriver(), OtherPageObject.class);
}
@FindBy(css = "#text")
SelenideElement description;
@FindBy(css = "#button")
WebElement submit;
@FindBy(css = "#label")
SelenideElement productName;
@FindBy(css = "#link")
WebElement followMe;
public void useElements(){
description.getText();
submit.click();
productName.shouldBe(visible);
followMe.click();
}
}
KISS PageObject
public class KISSPageObject {
SelenideElement description=$("#text"),
submitBtn=$("#button"),
productName=$("#label"),
followMe=$("#link");
public void useElements(){
description.getText();
submitBtn.click();
productName.shouldBe(visible);
followMe.click();
}
}
Selenium PageObject
// will not work, because Selenium doesn't use Lazy WebElement evaluation
public class SeleniumPageObjectNotWorking {
WebDriver driver=WebDriverRunner.getWebDriver();
// search will start immediately during class/object initialization
WebElement description=driver.findElement(By.cssSelector("#text")),
submitBtn=driver.findElement(By.cssSelector("#text")),
productName=driver.findElement(By.cssSelector("#text")),
followMe=driver.findElement(By.cssSelector("#link"));
public void useElements(){
description.getText();
submitBtn.click();
productName.getText();
followMe.click();
}
}
Selenium PageObject
// will work
public class SeleniumPageObjectWorking {
WebDriver driver=WebDriverRunner.getWebDriver();
By description=By.cssSelector("#text"),
submitBtn=By.cssSelector("#button"),
productName=By.cssSelector("#label"),
followMe=By.cssSelector("#link");
public void useElements(){
driver.findElement(description).getText();
driver.findElement(submitBtn).click();
driver.findElement(productName).getText();
driver.findElement(followMe).click();
}
}
Fluent PageObjects
Test
public class OtherTest {
@Test
public void testProductName(){
new OtherSearchPage().search()
.showDetails()
.productName.shouldBe(visible);
}
}
public class OtherSearchPage {
private SelenideElement search =$("#button");
public OtherDetailsPage search(){
search.click();
return new OtherDetailsPage();
}
}



public class OtherDetailsPage {
public SelenideElement productName=$("#label");
private SelenideElement details =$("#link");
public OtherDetailsPage showDetails(){
details.click();
return this;
}
}
Page Objects
Razor
Понятие переходов между "страницами"
Какие "страницы"?
SinglePageApplication (SPA)
Бывают исключения!
Portals with widgets
public class KISSSearchWidget {
private SelenideElement search =$("#button");
public void search(){
search.click();
}
}
public class KISSDetailsWidget {
public SelenideElement productName=$("#label");
private SelenideElement details =$("#link");
public void showDetails(){
details.click();
}
}
KISS Page Widgets
public class KISSTest {
@Test
public void testProductName(){
new KISSSearchWidget().search();
KISSDetailsWidget kissDetailsWidget = new KISSDetailsWidget();
kissDetailsWidget.showDetails();
kissDetailsWidget.productName.shouldBe(visible);
// also okay
new KISSDetailsWidget().showDetails();
new KISSDetailsWidget().productName.shouldBe(visible);
}
}
KISS Test
Test
public class PaymentsTest {
@Test
public void testPaymentProcess(){
new PaymentWizard().confirm()
.fillRecipientData()
.confirm()
.fillAmountData()
.confirm()
.enterTAN()
.confirm();
}
}
Inheritance
accountNo: 123456


balance: -156,12 €
accountNo: 786312


interest: 2% p.a.
Checking Account Saving Account
UI
details details
public class CheckingAccountPage extends BaseBankAccountPage{
public SelenideElement balance=$("#balance");
}
public class SavingAccountPage extends BaseBankAccountPage{
public SelenideElement interestRate=$("#interest");
}
Page Objects
public class BaseBankAccountPage {
public SelenideElement accountNumber=$("#account");
private SelenideElement details =$("#link");
public void showDetails(){
details.click();
}
}
Base Page Object


balance: -156,12 €


interest: 2% p.a.
Checking Account Saving Account
accountNo: 123456


Base Account
details
A bit of duplication is better than
a bit of dependency
Rob Pike
(https://www.youtube.com/watch?v=PAAkCSZUG1c&t=9m28s)
public class KISSCheckingAccountPage {
public SelenideElement accountNumber=$("#account");
public SelenideElement balance=$("#balance");
private SelenideElement details =$("#link");
public void showDetails(){
details.click();
}
}
public class KISSSavingAccountPage {
public SelenideElement accountNumber=$("#account");
public SelenideElement interest=$("#interest");
private SelenideElement details =$("#link");
public void showDetails(){
details.click();
}
}
KISS Page Objects
public class InheritedTest extends BaseTest{
@Test
public void testSomething(){
$("#button").click();
$("#text").shouldBe(visible);
}
}
Inherited Tests
public class BaseTest {
@Before
public void openSite() {
open("http://example.com");
login("username","pwd");
}
}
Base Test
public class KISSTest{
@Before
public void openSite() {
open("http://example.com");
login("username","pwd");
}
@Test
public void testSomething(){
$("#button").click();
$("#text").shouldBe(visible);
}
}
KISS Tests
Good inheritance-free tests possible
Composition
Composition over Inheritance
header
accountNo: 123456


balance: -156,12 €
footer
Checking Account Saving Account
UI
details
header
accountNo: 123456


interest: 3,5% p.a
footer
details
// KISS Widget
public class Header {
public SelenideElement menu=$("#menu");
public SelenideElement logo=$("#logo");
}


// KISS Widget
public class Footer {
public ElementsCollection links=$$("#links");
public SelenideElement copyright=$("#copyright");
}
KISS Page Widgets
public class CompositionSavingAccountPage {
public Header header;
public Footer footer;
public SelenideElement accountNumber=$("#account");
public SelenideElement interest=$("#interest");
private SelenideElement details =$("#link");
public void showDetails(){
details.click();
}
}
Page Object
public class CompositionTest {
@Test
public void testDetails(){
new CompositionSavingAccountPage().showDetails();
new CompositionSavingAccountPage().accountNumber.shouldHave(text("12345"));
}
@Test
public void testFooterAndHeader(){
CompositionSavingAccountPage savingAccountPage = new CompositionSavingAccountPage();
savingAccountPage.showDetails();
savingAccountPage.header.logo.shouldBe(visible);
savingAccountPage.footer.links.shouldHaveSize(5);
}
}
Test
public class CompositionSavingAccountPage {
public Header header;
public Footer footer;
public SelenideElement accountNumber=$("#account");
public SelenideElement interest=$("#interest");
private SelenideElement details =$("#link");
public void showDetails(){
details.click();
}
}
Page Object
public class KISSSavingAccountWidget {
public SelenideElement accountNumber=$("#account");
public SelenideElement interest=$("#interest");
private SelenideElement details =$("#link");
public void showDetails(){
details.click();
}
}
KISS Page Object Widget
public class KISSTest {
@Test
public void testDetails(){
new KISSSavingAccountWidget().showDetails();
new KISSSavingAccountWidget().accountNumber.shouldHave(text("12345"));
}
@Test
public void testFooterAndHeader(){
new KISSSavingAccountWidget().showDetails();
new Header().logo.shouldBe(visible);
new Footer().links.shouldHaveSize(5);
}
}
KISS Tests
Test data and constants
public class OtherTest {
@Test
public void testInput(){
$("#firstname").setValue("Alexei");
$("#lastname").setValue("Vinogradov");
$("#submit").click();
$("#fullname").shouldHave(exactText("Alexei Vinogradov"));
}
@Test
public void testSearchField(){
$("#search").setValue("Vinogradov");
$("#submit").click();
$("#fullname").shouldHave(exactText("Alexei Vinogradov"));
}
}
Test
public class OtherTest {
public static final String FIRSTNAME = "Alexei";
public static final String LASTNAME = "Vinogradov";
@Test
public void testInput(){
$("#firstname").setValue(FIRSTNAME);
$("#lastname").setValue(LASTNAME);
$("#submit").click();
$("#fullname").shouldHave(exactText(FIRSTNAME+" "+LASTNAME));
}
@Test
public void testSearchField(){
$("#search").setValue(LASTNAME);
$("#submit").click();
$("#fullname").shouldHave(exactText(FIRSTNAME+" "+LASTNAME));
}
}
Test
public class KISSTest {
@Test
public void testInput(){
$("#firstname").setValue("Alexei");
$("#lastname").setValue("Vinogradov");
$("#submit").click();
$("#fullname").shouldHave(exactText("Alexei Vinogradov"));
}
@Test
public void testSearchField(){
$("#search").setValue("Vinogradov");
$("#submit").click();
$("#fullname").shouldHave(exactText("Alexei Vinogradov"));
}
}
KISS Test
Locators (Single Responsibility)
public class OtherPageObject {
SelenideElement description=Locators.DESCRIPTION,
submitBtn=Locators.SUBMIT,
productName=Locators.LABEL;
public void useElements(){
description.getText();
submitBtn.click();
productName.shouldBe(visible);
}
}
Page Object
public class Locators {
public final static SelenideElement DESCRIPTION=$("#description");
public final static SelenideElement SUBMIT=$("#submit");
public final static SelenideElement LABEL=$("#label");
}
public class OtherPageObject {
SelenideElement description=Locators.DESCRIPTION,
submitBtn=Locators.SUBMIT,
productName=Locators.LABEL;
public void useElements(){
description.getText();
submitBtn.click();
productName.shouldBe(visible);
}
}
Page Object
public class Locators {
public final static SelenideElement HOMEPAGE_HEADER_DESCRIPTION=$("#description");
public final static SelenideElement PRODUCTPAGE_SEARCH_SUBMIT=$("#submit");
public final static SelenideElement ACCOUNTTABLE_COLUMN_PRICE_LABEL=$("#label");
}
public class OtherPageObject {
SelenideElement description=Locators.DESCRIPTION,
submitBtn=Locators.SUBMIT,
productName=Locators.LABEL;
public void useElements(){
description.getText();
submitBtn.click();
productName.shouldBe(visible);
}
}
Page Object
public class Locators {
public final static SelenideElement DESCRIPTION=$("#description");
public final static SelenideElement SUBMIT=$("#submit");
public final static SelenideElement LABEL=$("#label");
}
public class KISSPageObject {
SelenideElement description=$("#text"),
submitBtn=$("#button"),
productName=$("#label");
public void useElements(){
description.getText();
submitBtn.click();
productName.shouldBe(visible);
}
}
KISS PageObject
JavaDoc & Comments
public class OtherPageObject {
SelenideElement element=$("#element",1);
public SelenideElement findUser(String name, int row){
<…>
}
}
PageObject
public class OtherPageObject {
SelenideElement element=$("#element",1); // there are 3 #element-s, take second
/**
* Finds user in the table row
*/
public SelenideElement findUser(String name, int row){
<…>
}
}
PageObject
public class KISSPageObject {
SelenideElement element=$("#element",1); // there are 3 #element-s, take second
/**
* Finds user in the table row, substring will be searched
* @param name case-sensitive, substring
* @param row 0..N
* @return SelenideElement representing a table row or null if not found.
*/
public SelenideElement findUser(String name, int row){
<…>
}
}
PageObject
public class KISSPageObject {
SelenideElement element=$("#element",1); // there are 3 #element-s, take second
/**
* Finds user in the table row, substring will be searched
* Examples: findUser("lex",0) - finds Alexei in the first row
* findUser("alex",0) - returns null, if Alexei in the first row
* @param name case-sensitive, substring
* @param row 0..N
* @return SelenideElement representing a table row or null if not found.
*/
public SelenideElement findUser(String name, int row){
<…>
}
}
PageObject
Стоимость одного теста
KISS и сокращение ненужных сущностей
упрощает чтение и поддержку кода



Код становится скучным и простым
Простой прямолинейный код 

просто и прямолинейно поддерживать
Не значит, что SOLID, DRY, Design Patterns
плохие!
Не значит, что их никогда не надо
использовать!
ПРОСТОТА
The End.
Questions?
skype: alexejv

email: qa@vinogradov-it.de
twitter: @vinogradoff

More Related Content

What's hot

Selectors & Traversing
Selectors & TraversingSelectors & Traversing
Selectors & Traversingswainet
 
Chief Keef's hologram can't catch a break, and it's a win for Keef
Chief Keef's hologram can't catch a break, and it's a win for KeefChief Keef's hologram can't catch a break, and it's a win for Keef
Chief Keef's hologram can't catch a break, and it's a win for Keefchicagonewsonlineradio
 
Webstandard2007 Spry Widget Ver1
Webstandard2007  Spry Widget Ver1Webstandard2007  Spry Widget Ver1
Webstandard2007 Spry Widget Ver1真一 藤川
 
2015 Key Ingredient Cook-Off
2015 Key Ingredient Cook-Off2015 Key Ingredient Cook-Off
2015 Key Ingredient Cook-Offirwinvifxcfesre
 
EWD 3トレーニングコース#13 全てをひとつにまとめてewd-xpressで稼働させてみる
EWD 3トレーニングコース#13 全てをひとつにまとめてewd-xpressで稼働させてみるEWD 3トレーニングコース#13 全てをひとつにまとめてewd-xpressで稼働させてみる
EWD 3トレーニングコース#13 全てをひとつにまとめてewd-xpressで稼働させてみるKiyoshi Sawada
 
玩玩jquery
玩玩jquery玩玩jquery
玩玩jquerySimon Su
 
Hands-On Java web passando por Servlets, JSP, JSTL, JDBC, Hibernate, DAO, MV...
Hands-On Java web passando por  Servlets, JSP, JSTL, JDBC, Hibernate, DAO, MV...Hands-On Java web passando por  Servlets, JSP, JSTL, JDBC, Hibernate, DAO, MV...
Hands-On Java web passando por Servlets, JSP, JSTL, JDBC, Hibernate, DAO, MV...Mario Jorge Pereira
 
jQueryUI: Rich Interactivity, Simplified
jQueryUI: Rich Interactivity, SimplifiedjQueryUI: Rich Interactivity, Simplified
jQueryUI: Rich Interactivity, Simplifiedmikehostetler
 
Check out our photos of the Pixies' Metro show
Check out our photos of the Pixies' Metro showCheck out our photos of the Pixies' Metro show
Check out our photos of the Pixies' Metro showchicagonewsyesterday
 

What's hot (12)

Get more votes!
Get more votes!Get more votes!
Get more votes!
 
Get more votes!
Get more votes!Get more votes!
Get more votes!
 
Selectors & Traversing
Selectors & TraversingSelectors & Traversing
Selectors & Traversing
 
Chief Keef's hologram can't catch a break, and it's a win for Keef
Chief Keef's hologram can't catch a break, and it's a win for KeefChief Keef's hologram can't catch a break, and it's a win for Keef
Chief Keef's hologram can't catch a break, and it's a win for Keef
 
Best hotel
Best hotelBest hotel
Best hotel
 
Webstandard2007 Spry Widget Ver1
Webstandard2007  Spry Widget Ver1Webstandard2007  Spry Widget Ver1
Webstandard2007 Spry Widget Ver1
 
2015 Key Ingredient Cook-Off
2015 Key Ingredient Cook-Off2015 Key Ingredient Cook-Off
2015 Key Ingredient Cook-Off
 
EWD 3トレーニングコース#13 全てをひとつにまとめてewd-xpressで稼働させてみる
EWD 3トレーニングコース#13 全てをひとつにまとめてewd-xpressで稼働させてみるEWD 3トレーニングコース#13 全てをひとつにまとめてewd-xpressで稼働させてみる
EWD 3トレーニングコース#13 全てをひとつにまとめてewd-xpressで稼働させてみる
 
玩玩jquery
玩玩jquery玩玩jquery
玩玩jquery
 
Hands-On Java web passando por Servlets, JSP, JSTL, JDBC, Hibernate, DAO, MV...
Hands-On Java web passando por  Servlets, JSP, JSTL, JDBC, Hibernate, DAO, MV...Hands-On Java web passando por  Servlets, JSP, JSTL, JDBC, Hibernate, DAO, MV...
Hands-On Java web passando por Servlets, JSP, JSTL, JDBC, Hibernate, DAO, MV...
 
jQueryUI: Rich Interactivity, Simplified
jQueryUI: Rich Interactivity, SimplifiedjQueryUI: Rich Interactivity, Simplified
jQueryUI: Rich Interactivity, Simplified
 
Check out our photos of the Pixies' Metro show
Check out our photos of the Pixies' Metro showCheck out our photos of the Pixies' Metro show
Check out our photos of the Pixies' Metro show
 

More from Ievgenii Katsan

8 andrew kalyuzhin - 30 ux-advices, that will make users love you
8   andrew kalyuzhin - 30 ux-advices, that will make users love you8   andrew kalyuzhin - 30 ux-advices, that will make users love you
8 andrew kalyuzhin - 30 ux-advices, that will make users love youIevgenii Katsan
 
5 hans van loenhoud - master-class the 7 skills of highly successful teams
5   hans van loenhoud - master-class the 7 skills of highly successful teams5   hans van loenhoud - master-class the 7 skills of highly successful teams
5 hans van loenhoud - master-class the 7 skills of highly successful teamsIevgenii Katsan
 
4 alexey orlov - life of product in startup and enterprise
4   alexey orlov - life of product in startup and enterprise4   alexey orlov - life of product in startup and enterprise
4 alexey orlov - life of product in startup and enterpriseIevgenii Katsan
 
3 dmitry gomeniuk - how to make data-driven decisions in saa s products
3   dmitry gomeniuk - how to make data-driven decisions in saa s products3   dmitry gomeniuk - how to make data-driven decisions in saa s products
3 dmitry gomeniuk - how to make data-driven decisions in saa s productsIevgenii Katsan
 
7 hans van loenhoud - the problem-goal-solution trinity
7   hans van loenhoud - the problem-goal-solution trinity7   hans van loenhoud - the problem-goal-solution trinity
7 hans van loenhoud - the problem-goal-solution trinityIevgenii Katsan
 
3 denys gobov - change request specification the knowledge base or the task...
3   denys gobov - change request specification the knowledge base or the task...3   denys gobov - change request specification the knowledge base or the task...
3 denys gobov - change request specification the knowledge base or the task...Ievgenii Katsan
 
5 victoria cupet - learn to play business analysis
5   victoria cupet - learn to play business analysis5   victoria cupet - learn to play business analysis
5 victoria cupet - learn to play business analysisIevgenii Katsan
 
5 alina petrenko - key requirements elicitation during the first contact wi...
5   alina petrenko - key requirements elicitation during the first contact wi...5   alina petrenko - key requirements elicitation during the first contact wi...
5 alina petrenko - key requirements elicitation during the first contact wi...Ievgenii Katsan
 
3 karabak kuyavets transformation of business analyst to product owner
3   karabak kuyavets transformation of business analyst to product owner3   karabak kuyavets transformation of business analyst to product owner
3 karabak kuyavets transformation of business analyst to product ownerIevgenii Katsan
 
4 andrii melnykov - stakeholder management for pd ms and b-as and why it is...
4   andrii melnykov - stakeholder management for pd ms and b-as and why it is...4   andrii melnykov - stakeholder management for pd ms and b-as and why it is...
4 andrii melnykov - stakeholder management for pd ms and b-as and why it is...Ievgenii Katsan
 
3 zornitsa nikolova - the product manager between decision making and facil...
3   zornitsa nikolova - the product manager between decision making and facil...3   zornitsa nikolova - the product manager between decision making and facil...
3 zornitsa nikolova - the product manager between decision making and facil...Ievgenii Katsan
 
4 viktoriya gudym - how to effectively manage remote employees
4   viktoriya gudym - how to effectively manage remote employees4   viktoriya gudym - how to effectively manage remote employees
4 viktoriya gudym - how to effectively manage remote employeesIevgenii Katsan
 
9 natali renska - product and outsource development, how to cook 2 meals in...
9   natali renska - product and outsource development, how to cook 2 meals in...9   natali renska - product and outsource development, how to cook 2 meals in...
9 natali renska - product and outsource development, how to cook 2 meals in...Ievgenii Katsan
 
7 denis parkhomenko - from idea to execution how to make a product that cus...
7   denis parkhomenko - from idea to execution how to make a product that cus...7   denis parkhomenko - from idea to execution how to make a product that cus...
7 denis parkhomenko - from idea to execution how to make a product that cus...Ievgenii Katsan
 
6 anton vitiaz - inside the mvp in 3 days
6   anton vitiaz - inside the mvp in 3 days6   anton vitiaz - inside the mvp in 3 days
6 anton vitiaz - inside the mvp in 3 daysIevgenii Katsan
 
5 mariya popova - ideal product management. unicorns in our reality
5   mariya popova - ideal product management. unicorns in our reality5   mariya popova - ideal product management. unicorns in our reality
5 mariya popova - ideal product management. unicorns in our realityIevgenii Katsan
 
2 victor podzubanov - design thinking game
2   victor podzubanov - design thinking game2   victor podzubanov - design thinking game
2 victor podzubanov - design thinking gameIevgenii Katsan
 
3 sergiy potapov - analyst to product owner
3   sergiy potapov - analyst to product owner3   sergiy potapov - analyst to product owner
3 sergiy potapov - analyst to product ownerIevgenii Katsan
 
4 anton parkhomenko - how to make effective user research with no budget at...
4   anton parkhomenko - how to make effective user research with no budget at...4   anton parkhomenko - how to make effective user research with no budget at...
4 anton parkhomenko - how to make effective user research with no budget at...Ievgenii Katsan
 

More from Ievgenii Katsan (20)

8 andrew kalyuzhin - 30 ux-advices, that will make users love you
8   andrew kalyuzhin - 30 ux-advices, that will make users love you8   andrew kalyuzhin - 30 ux-advices, that will make users love you
8 andrew kalyuzhin - 30 ux-advices, that will make users love you
 
5 hans van loenhoud - master-class the 7 skills of highly successful teams
5   hans van loenhoud - master-class the 7 skills of highly successful teams5   hans van loenhoud - master-class the 7 skills of highly successful teams
5 hans van loenhoud - master-class the 7 skills of highly successful teams
 
4 alexey orlov - life of product in startup and enterprise
4   alexey orlov - life of product in startup and enterprise4   alexey orlov - life of product in startup and enterprise
4 alexey orlov - life of product in startup and enterprise
 
3 dmitry gomeniuk - how to make data-driven decisions in saa s products
3   dmitry gomeniuk - how to make data-driven decisions in saa s products3   dmitry gomeniuk - how to make data-driven decisions in saa s products
3 dmitry gomeniuk - how to make data-driven decisions in saa s products
 
7 hans van loenhoud - the problem-goal-solution trinity
7   hans van loenhoud - the problem-goal-solution trinity7   hans van loenhoud - the problem-goal-solution trinity
7 hans van loenhoud - the problem-goal-solution trinity
 
1 hans van loenhoud -
1   hans van loenhoud - 1   hans van loenhoud -
1 hans van loenhoud -
 
3 denys gobov - change request specification the knowledge base or the task...
3   denys gobov - change request specification the knowledge base or the task...3   denys gobov - change request specification the knowledge base or the task...
3 denys gobov - change request specification the knowledge base or the task...
 
5 victoria cupet - learn to play business analysis
5   victoria cupet - learn to play business analysis5   victoria cupet - learn to play business analysis
5 victoria cupet - learn to play business analysis
 
5 alina petrenko - key requirements elicitation during the first contact wi...
5   alina petrenko - key requirements elicitation during the first contact wi...5   alina petrenko - key requirements elicitation during the first contact wi...
5 alina petrenko - key requirements elicitation during the first contact wi...
 
3 karabak kuyavets transformation of business analyst to product owner
3   karabak kuyavets transformation of business analyst to product owner3   karabak kuyavets transformation of business analyst to product owner
3 karabak kuyavets transformation of business analyst to product owner
 
4 andrii melnykov - stakeholder management for pd ms and b-as and why it is...
4   andrii melnykov - stakeholder management for pd ms and b-as and why it is...4   andrii melnykov - stakeholder management for pd ms and b-as and why it is...
4 andrii melnykov - stakeholder management for pd ms and b-as and why it is...
 
3 zornitsa nikolova - the product manager between decision making and facil...
3   zornitsa nikolova - the product manager between decision making and facil...3   zornitsa nikolova - the product manager between decision making and facil...
3 zornitsa nikolova - the product manager between decision making and facil...
 
4 viktoriya gudym - how to effectively manage remote employees
4   viktoriya gudym - how to effectively manage remote employees4   viktoriya gudym - how to effectively manage remote employees
4 viktoriya gudym - how to effectively manage remote employees
 
9 natali renska - product and outsource development, how to cook 2 meals in...
9   natali renska - product and outsource development, how to cook 2 meals in...9   natali renska - product and outsource development, how to cook 2 meals in...
9 natali renska - product and outsource development, how to cook 2 meals in...
 
7 denis parkhomenko - from idea to execution how to make a product that cus...
7   denis parkhomenko - from idea to execution how to make a product that cus...7   denis parkhomenko - from idea to execution how to make a product that cus...
7 denis parkhomenko - from idea to execution how to make a product that cus...
 
6 anton vitiaz - inside the mvp in 3 days
6   anton vitiaz - inside the mvp in 3 days6   anton vitiaz - inside the mvp in 3 days
6 anton vitiaz - inside the mvp in 3 days
 
5 mariya popova - ideal product management. unicorns in our reality
5   mariya popova - ideal product management. unicorns in our reality5   mariya popova - ideal product management. unicorns in our reality
5 mariya popova - ideal product management. unicorns in our reality
 
2 victor podzubanov - design thinking game
2   victor podzubanov - design thinking game2   victor podzubanov - design thinking game
2 victor podzubanov - design thinking game
 
3 sergiy potapov - analyst to product owner
3   sergiy potapov - analyst to product owner3   sergiy potapov - analyst to product owner
3 sergiy potapov - analyst to product owner
 
4 anton parkhomenko - how to make effective user research with no budget at...
4   anton parkhomenko - how to make effective user research with no budget at...4   anton parkhomenko - how to make effective user research with no budget at...
4 anton parkhomenko - how to make effective user research with no budget at...
 

Occam razor kiss testing stage

  • 1. KISS-Driven Test Automation по лезвию бритвы Оккама Alexei VinogradovMaster class
  • 2. KISS-Driven Test Automation по лезу бритви Оккама Олекciй ВiноградовМайстер-клас
  • 3. Олекciй Вiноградов
 IT-Kонсультант
 
 тестування, управління тестуванням, автоматизація в тестуванні, коучинг В області IT c 1998, доповідач c 2014 року Студент-стажер -> Програмiст -> Тестувальник ->…
  • 5.
  • 7. Мой личный опыт 
 отличается от вашего Для всех правил из этого доклада есть исключения
  • 8. Личный опыт любого докладчика
 отличается от вашего Для всех правил из всех докладов есть исключения
  • 9.
  • 10. The best since the 14. century
  • 11. "Бритва Оккама" Уильям Оккам
 1285 - 1349, Англия Не следует привлекать
 новые сущности без крайней
 на то необходимости
  • 12. «Что может быть сделано на основе меньшего числа [предположений], не следует делать, исходя из большего» Не аксиома, а предложение X <- A, B, C X <- A, B, C, D
  • 14. Лучший код - это ненаписанный код
  • 16.
  • 18. 1. Начинай с KISS 2. Продолжай KISS 3. Отказывайся от KISS только по хорошим причинам Три принципа KDTA
  • 20. KISS over Design Patterns
  • 24. Сложнее всего обходится без кода совсем
  • 28. Не надо писать фреймворки*, надо писать тесты *на работе
  • 30.
  • 34.
  • 35.
  • 36. PageObject public class OtherPageObject{ Text description=new Text(By.cssSelector("#text")); Button submit=new Button(By.cssSelector("#button")); Label productName=new Label(By.cssSelector("#label")); Link followMe=new Link(By.cssSelector("#link")); TextField passwordField=new TextField(By.cssSelector("#textfield")); public void useElements(){ description.getText(); submit.click(); productName.shouldBe(visible); followMe.click(); passwordField.setValue("password"); } }
  • 37. PageObject public class OtherPageObject{ Text description=new Text(By.cssSelector("#text")); Button submit=new Button(By.cssSelector("#button")); Label productName=new Label(By.cssSelector("#label")); Link followMe=new Link(By.cssSelector("#link")); TextField passwordField=new TextField(By.cssSelector("#textfield")); public void useElements(){ description.getText(); submit.click(); productName.shouldBe(visible); followMe.click(); passwordField.setValue("password"); } }
  • 38. KISS PageObject public class KISSPageObject { SelenideElement description=$("#text"), submitBtn=$("#button"), productName=$("#label"), followMe=$("#link"), passwordField=$("#textfield"); public void useElements(){ description.getText(); submitBtn.click(); productName.shouldBe(visible); followMe.click(); passwordField.setValue("password"); } }
  • 40.
  • 41. PageObject public class OtherPageObject { private SelenideElement productName=$("#label"); public SelenideElement getProductName() { return productName; } private SelenideElement submitBtn=$("#button"); private SelenideElement followMe=$("#link"); public void useElements(){ submitBtn.click(); followMe.click(); } }
  • 42. Test public class OtherTest { @Test public void testProductName(){ new OtherPageObject().getProductName().shouldBe(visible); } }
  • 43. PageObject public class OtherPageObject { private SelenideElement productName=$("#label"); public SelenideElement getProductName() { return productName; } private SelenideElement submitBtn=$("#button"); private SelenideElement followMe=$("#link"); public void useElements(){ submitBtn.click(); followMe.click(); } }
  • 44. KISS PageObject public class KISSPageObject { public SelenideElement productName=$("#label"); private SelenideElement submitBtn=$("#button"); private SelenideElement followMe=$("#link"); public void useElements(){ submitBtn.click(); followMe.click(); } }
  • 45. Test public class KISSTest { @Test public void testProductName(){ new OtherPageObject().productName.shouldBe(visible); } }
  • 46. Lombok PageObject public class LombokPageObject { @Getter private SelenideElement productName=$("#label"); //Lombok 
 private SelenideElement submitBtn=$("#button"); private SelenideElement followMe=$("#link"); public void useElements(){ submitBtn.click(); followMe.click(); } }
  • 47. Lombok PageObject public class LombokPageObject { @Getter private SelenideElement productName=$("#label"); //Lombok 
 private SelenideElement submitBtn=$("#button"); private SelenideElement followMe=$("#link"); public void useElements(){ submitBtn.click(); followMe.click(); } }
  • 49. PageObject public class OtherPageObject { @FindBy(css = "#text") SelenideElement description; @FindBy(css = "#button") WebElement submit; @FindBy(css = "#label") SelenideElement productName; @FindBy(css = "#link") WebElement followMe; public void useElements(){ description.getText(); submit.click(); productName.shouldBe(visible); followMe.click(); } }
  • 50. PageObject public class OtherPageObject { public OtherPageObject(){ PageFactory.initElements(WebDriverRunner.getWebDriver(), OtherPageObject.class); } @FindBy(css = "#text") SelenideElement description; @FindBy(css = "#button") WebElement submit; @FindBy(css = "#label") SelenideElement productName; @FindBy(css = "#link") WebElement followMe; public void useElements(){ description.getText(); submit.click(); productName.shouldBe(visible); followMe.click(); } }
  • 51. KISS PageObject public class KISSPageObject { SelenideElement description=$("#text"), submitBtn=$("#button"), productName=$("#label"), followMe=$("#link"); public void useElements(){ description.getText(); submitBtn.click(); productName.shouldBe(visible); followMe.click(); } }
  • 52. Selenium PageObject // will not work, because Selenium doesn't use Lazy WebElement evaluation public class SeleniumPageObjectNotWorking { WebDriver driver=WebDriverRunner.getWebDriver(); // search will start immediately during class/object initialization WebElement description=driver.findElement(By.cssSelector("#text")), submitBtn=driver.findElement(By.cssSelector("#text")), productName=driver.findElement(By.cssSelector("#text")), followMe=driver.findElement(By.cssSelector("#link")); public void useElements(){ description.getText(); submitBtn.click(); productName.getText(); followMe.click(); } }
  • 53. Selenium PageObject // will work public class SeleniumPageObjectWorking { WebDriver driver=WebDriverRunner.getWebDriver(); By description=By.cssSelector("#text"), submitBtn=By.cssSelector("#button"), productName=By.cssSelector("#label"), followMe=By.cssSelector("#link"); public void useElements(){ driver.findElement(description).getText(); driver.findElement(submitBtn).click(); driver.findElement(productName).getText(); driver.findElement(followMe).click(); } }
  • 55. Test public class OtherTest { @Test public void testProductName(){ new OtherSearchPage().search() .showDetails() .productName.shouldBe(visible); } }
  • 56. public class OtherSearchPage { private SelenideElement search =$("#button"); public OtherDetailsPage search(){ search.click(); return new OtherDetailsPage(); } }
 
 public class OtherDetailsPage { public SelenideElement productName=$("#label"); private SelenideElement details =$("#link"); public OtherDetailsPage showDetails(){ details.click(); return this; } } Page Objects
  • 57. Razor Понятие переходов между "страницами" Какие "страницы"? SinglePageApplication (SPA) Бывают исключения! Portals with widgets
  • 58. public class KISSSearchWidget { private SelenideElement search =$("#button"); public void search(){ search.click(); } } public class KISSDetailsWidget { public SelenideElement productName=$("#label"); private SelenideElement details =$("#link"); public void showDetails(){ details.click(); } } KISS Page Widgets
  • 59. public class KISSTest { @Test public void testProductName(){ new KISSSearchWidget().search(); KISSDetailsWidget kissDetailsWidget = new KISSDetailsWidget(); kissDetailsWidget.showDetails(); kissDetailsWidget.productName.shouldBe(visible); // also okay new KISSDetailsWidget().showDetails(); new KISSDetailsWidget().productName.shouldBe(visible); } } KISS Test
  • 60. Test public class PaymentsTest { @Test public void testPaymentProcess(){ new PaymentWizard().confirm() .fillRecipientData() .confirm() .fillAmountData() .confirm() .enterTAN() .confirm(); } }
  • 62. accountNo: 123456 
 balance: -156,12 € accountNo: 786312 
 interest: 2% p.a. Checking Account Saving Account UI details details
  • 63. public class CheckingAccountPage extends BaseBankAccountPage{ public SelenideElement balance=$("#balance"); } public class SavingAccountPage extends BaseBankAccountPage{ public SelenideElement interestRate=$("#interest"); } Page Objects
  • 64. public class BaseBankAccountPage { public SelenideElement accountNumber=$("#account"); private SelenideElement details =$("#link"); public void showDetails(){ details.click(); } } Base Page Object
  • 65. 
 balance: -156,12 € 
 interest: 2% p.a. Checking Account Saving Account accountNo: 123456 
 Base Account details
  • 66. A bit of duplication is better than a bit of dependency Rob Pike (https://www.youtube.com/watch?v=PAAkCSZUG1c&t=9m28s)
  • 67. public class KISSCheckingAccountPage { public SelenideElement accountNumber=$("#account"); public SelenideElement balance=$("#balance"); private SelenideElement details =$("#link"); public void showDetails(){ details.click(); } } public class KISSSavingAccountPage { public SelenideElement accountNumber=$("#account"); public SelenideElement interest=$("#interest"); private SelenideElement details =$("#link"); public void showDetails(){ details.click(); } } KISS Page Objects
  • 68. public class InheritedTest extends BaseTest{ @Test public void testSomething(){ $("#button").click(); $("#text").shouldBe(visible); } } Inherited Tests
  • 69. public class BaseTest { @Before public void openSite() { open("http://example.com"); login("username","pwd"); } } Base Test
  • 70. public class KISSTest{ @Before public void openSite() { open("http://example.com"); login("username","pwd"); } @Test public void testSomething(){ $("#button").click(); $("#text").shouldBe(visible); } } KISS Tests
  • 74. header accountNo: 123456 
 balance: -156,12 € footer Checking Account Saving Account UI details header accountNo: 123456 
 interest: 3,5% p.a footer details
  • 75. // KISS Widget public class Header { public SelenideElement menu=$("#menu"); public SelenideElement logo=$("#logo"); } 
 // KISS Widget public class Footer { public ElementsCollection links=$$("#links"); public SelenideElement copyright=$("#copyright"); } KISS Page Widgets
  • 76. public class CompositionSavingAccountPage { public Header header; public Footer footer; public SelenideElement accountNumber=$("#account"); public SelenideElement interest=$("#interest"); private SelenideElement details =$("#link"); public void showDetails(){ details.click(); } } Page Object
  • 77. public class CompositionTest { @Test public void testDetails(){ new CompositionSavingAccountPage().showDetails(); new CompositionSavingAccountPage().accountNumber.shouldHave(text("12345")); } @Test public void testFooterAndHeader(){ CompositionSavingAccountPage savingAccountPage = new CompositionSavingAccountPage(); savingAccountPage.showDetails(); savingAccountPage.header.logo.shouldBe(visible); savingAccountPage.footer.links.shouldHaveSize(5); } } Test
  • 78. public class CompositionSavingAccountPage { public Header header; public Footer footer; public SelenideElement accountNumber=$("#account"); public SelenideElement interest=$("#interest"); private SelenideElement details =$("#link"); public void showDetails(){ details.click(); } } Page Object
  • 79. public class KISSSavingAccountWidget { public SelenideElement accountNumber=$("#account"); public SelenideElement interest=$("#interest"); private SelenideElement details =$("#link"); public void showDetails(){ details.click(); } } KISS Page Object Widget
  • 80. public class KISSTest { @Test public void testDetails(){ new KISSSavingAccountWidget().showDetails(); new KISSSavingAccountWidget().accountNumber.shouldHave(text("12345")); } @Test public void testFooterAndHeader(){ new KISSSavingAccountWidget().showDetails(); new Header().logo.shouldBe(visible); new Footer().links.shouldHaveSize(5); } } KISS Tests
  • 81.
  • 82.
  • 83. Test data and constants
  • 84. public class OtherTest { @Test public void testInput(){ $("#firstname").setValue("Alexei"); $("#lastname").setValue("Vinogradov"); $("#submit").click(); $("#fullname").shouldHave(exactText("Alexei Vinogradov")); } @Test public void testSearchField(){ $("#search").setValue("Vinogradov"); $("#submit").click(); $("#fullname").shouldHave(exactText("Alexei Vinogradov")); } } Test
  • 85. public class OtherTest { public static final String FIRSTNAME = "Alexei"; public static final String LASTNAME = "Vinogradov"; @Test public void testInput(){ $("#firstname").setValue(FIRSTNAME); $("#lastname").setValue(LASTNAME); $("#submit").click(); $("#fullname").shouldHave(exactText(FIRSTNAME+" "+LASTNAME)); } @Test public void testSearchField(){ $("#search").setValue(LASTNAME); $("#submit").click(); $("#fullname").shouldHave(exactText(FIRSTNAME+" "+LASTNAME)); } } Test
  • 86. public class KISSTest { @Test public void testInput(){ $("#firstname").setValue("Alexei"); $("#lastname").setValue("Vinogradov"); $("#submit").click(); $("#fullname").shouldHave(exactText("Alexei Vinogradov")); } @Test public void testSearchField(){ $("#search").setValue("Vinogradov"); $("#submit").click(); $("#fullname").shouldHave(exactText("Alexei Vinogradov")); } } KISS Test
  • 88. public class OtherPageObject { SelenideElement description=Locators.DESCRIPTION, submitBtn=Locators.SUBMIT, productName=Locators.LABEL; public void useElements(){ description.getText(); submitBtn.click(); productName.shouldBe(visible); } } Page Object public class Locators { public final static SelenideElement DESCRIPTION=$("#description"); public final static SelenideElement SUBMIT=$("#submit"); public final static SelenideElement LABEL=$("#label"); }
  • 89. public class OtherPageObject { SelenideElement description=Locators.DESCRIPTION, submitBtn=Locators.SUBMIT, productName=Locators.LABEL; public void useElements(){ description.getText(); submitBtn.click(); productName.shouldBe(visible); } } Page Object public class Locators { public final static SelenideElement HOMEPAGE_HEADER_DESCRIPTION=$("#description"); public final static SelenideElement PRODUCTPAGE_SEARCH_SUBMIT=$("#submit"); public final static SelenideElement ACCOUNTTABLE_COLUMN_PRICE_LABEL=$("#label"); }
  • 90.
  • 91. public class OtherPageObject { SelenideElement description=Locators.DESCRIPTION, submitBtn=Locators.SUBMIT, productName=Locators.LABEL; public void useElements(){ description.getText(); submitBtn.click(); productName.shouldBe(visible); } } Page Object public class Locators { public final static SelenideElement DESCRIPTION=$("#description"); public final static SelenideElement SUBMIT=$("#submit"); public final static SelenideElement LABEL=$("#label"); }
  • 92. public class KISSPageObject { SelenideElement description=$("#text"), submitBtn=$("#button"), productName=$("#label"); public void useElements(){ description.getText(); submitBtn.click(); productName.shouldBe(visible); } } KISS PageObject
  • 94. public class OtherPageObject { SelenideElement element=$("#element",1); public SelenideElement findUser(String name, int row){ <…> } } PageObject
  • 95. public class OtherPageObject { SelenideElement element=$("#element",1); // there are 3 #element-s, take second /** * Finds user in the table row */ public SelenideElement findUser(String name, int row){ <…> } } PageObject
  • 96. public class KISSPageObject { SelenideElement element=$("#element",1); // there are 3 #element-s, take second /** * Finds user in the table row, substring will be searched * @param name case-sensitive, substring * @param row 0..N * @return SelenideElement representing a table row or null if not found. */ public SelenideElement findUser(String name, int row){ <…> } } PageObject
  • 97. public class KISSPageObject { SelenideElement element=$("#element",1); // there are 3 #element-s, take second /** * Finds user in the table row, substring will be searched * Examples: findUser("lex",0) - finds Alexei in the first row * findUser("alex",0) - returns null, if Alexei in the first row * @param name case-sensitive, substring * @param row 0..N * @return SelenideElement representing a table row or null if not found. */ public SelenideElement findUser(String name, int row){ <…> } } PageObject
  • 99.
  • 100. KISS и сокращение ненужных сущностей упрощает чтение и поддержку кода
 
 Код становится скучным и простым
  • 101.
  • 102.
  • 103.
  • 104. Простой прямолинейный код 
 просто и прямолинейно поддерживать
  • 105. Не значит, что SOLID, DRY, Design Patterns плохие! Не значит, что их никогда не надо использовать! ПРОСТОТА
  • 106.
  • 107. The End. Questions? skype: alexejv
 email: qa@vinogradov-it.de twitter: @vinogradoff