FEEL THE POWER OF THE JDI LIGHT JDI LIGHT 22 MARCH 2019
JDI EvangelistAlmost 14 years in TestingMore than 12 years in Test Automation ROMAN IOVLEV Chief QA Automation roman.Iovlev roman.Iovlev.jdi@gmail.com 2 http://roman-iovlev.info/
JDI FRAMEWORK JDI LightSimple to use test Automation FrameworkBased on UI Elements (Buttons, Forms etc.)Fast and stable tests executionUser friendly logs and reports with no effort [since 2015] JDNSimple and modern Page Objects Generator JDI DarkWeb Services testing Framework powered by Rest Assured 3 JDI MobileUI Test Framework for Mobile JDI DesktopUI Test Framework for Desktop
PAGE OBJECTS 4 HeaderMain Section MenuOdd Radio buttonsWeather Multiselect Name TextfieldPassport checkboxDescription textarea Composite Complex Simple
UI PAGE OBJECTS @JSite( “https://epam.github.io/JDI/ ")public class EpamSite { @Url ("/index.html”) public static HomePage homepage; public static ContactPage contactPage;} @Url("/contact/%s”) @Title(“Contact Form")public class ContactPage extends WebPage { @FindBy(id=“#Name”) public TextField name; @Css(“#LastName”) public TextField lastName; @UI(“[‘Submit’]”) public Button submit;} @BeforeSuite(alwaysRun = true)public static void setUp() { initElements(EpamGithubSite.class);} open()back()/ forward()refresh()/ reload()clearCache()/ addCookie()String getHtml()zoom(double factor) 5
UI ELEMENTS 6 public class LoginForm extends Form<User> { TextField login, password; Button enter;} @FindBy(id = “jdi-logo”)public static Image jdiLogo; @Css(“#colors”) public Dropdown colors @XPath(“//*[@id=‘nav’][text=‘%s’]”) public Menu navigation; @UI(“#nav[‘%s’]”) public Menu navigation;
HTML ELEMENTS HTML elements packBootstrap elementsAngular collectionsReact collectionsVue collectionsTelerik (KendoUI) https://epam.github.io/JDI/html5.html 7
LIST OF SECTIONS 8 List<Section> = Elements in JDI T get(String name/int index)MapArray<String, T> getMap()List<E> asData(Class<E> entityClass) Get Section by name/indexGet all elements as MapGet All elements as List of Sections public class SearchResult extends Section {@Title public WebElement label; public WebElement link;public WebElement description; } 8
LIST OF SECTIONS EXAMPLES @UI(“.g”) List<SearchResult> results; Assert.assertContains(results.get(5).description.getText(), “JDI some text”); results.get(“EPAM JDI”).link.click(); results.assertThat().value(expetedValue);results.assertThat().any(e -> e.name.toLowerCase().contains(“epam jdi"));results.assertThat().each(e -> e.name.toLowerCase().contains("jdi")) || e.description.contains(“jdi"));results.assertThat().onlyOne(e -> e.name.contains("EPAM JDI"));results.assertThat().noOne(e -> e.name.contains("SELENIDE")); 9
ELEMENT ASSERTS redButton.assertThat().text(is(“Submit”));notification.is().displayed();teaser.assertThat().text(containsString(“JDI is Awesome")) .cssClass(is(“jdi-teaser")).attr("type", is(“submit"));results.is().notEmpty();results.assertThat().values(hasItem(“JDI")); Powered by Hamcrest No more waits!Compare to Selenide and Selenium asserts 10
NO PAGE OBJECTS STYLE @Testpublic void nonPageObjectTest() { WebPage.openUrl("https://epam.github.io/JDI/index.html"); $("img#user-icon").click(); $("form #name").input("epam"); $("form #password").input("1234"); $("form [type=submit]").click(); Assert.assertEquals(WebPage.getUrl(), "https://epam.github.io/JDI/index.html");} 11
REDUCE AMOUNT OF CODE IN 5-30 TIMES 1
FORM EXAMPLE 13
REDUCE AMOUNT OF CODE public class ContactForm { @FindBy(xpath = "//div[contains(@class,'contacts')]//*[text()='Name']") public WebElement name; @FindBy(xpath = "//div[contains(@class,'contacts')]//*[text()='Family Name']") public WebElement familyName; @FindBy(xpath = "//div[contains(@class,'contacts')]//*[text()='Passport Id']") public WebElement passId; @FindBy(xpath = "//div[contains(@class,'contacts')]//*[text()='Passport Number']") public WebElement passNum; ... @FindBy(xpath = "//div[contains(@class,'contacts')]//*[text()=‘Accept’]") public WebElement accept; @FindBy(xpath = "//div[contains(@class,'contacts')]//button[text()='Submit']") public WebElement submitButton; public void submit(String name, String familyName, String passportId, String passportNumber, ...., Boolean accept) { this.name.sendKeys(name); this.familyName.sendKeys(familyName); this.passportId.sendKeys(passportId); this.passportNumber.sendKeys(passportNumber); ... if (accept && !this.accept.isSelected() || !accept && this.accept.isSelected()) this.accept.click(); submitButton.click(); }} 14
REDUCE AMOUNT OF CODE public class ContactForm { public void submit(String name, String familyName, String passportId, String passportNumber, ...., Boolean accept) { $("//div[contains(@class,'contacts')]//*[text()='Name']").sendKeys(name); $("//div[contains(@class,'contacts')]//*[text()='Family Name']").sendKeys(familyName); $("//div[contains(@class,'contacts')]//*[text()='Passport Id']").sendKeys(passportId); $(“//div[contains(@class,'contacts')]//*[text()='Passport Number']").sendKeys(passportNumber);... SelenideElement accept = $("//div[contains(@class,'contacts')]//*[text()=‘Accept’]") if (accept && !this.accept.isSelected() || !accept && this.accept.isSelected()) $("//div[contains(@class,'contacts')]//button[text()='Submit']") .click();} } 15 public void check(String name, String familyName, String passportId, String passportNumber, ...., Boolean accept) { assertEquals($("//div[contains(@class,'contacts')]//*[text()='Name']").getText(), name); assertEquals($("//div[contains(@class,'contacts')]//*[text()='Family Name']").getText(), familyName); assertEquals($("//div[contains(@class,'contacts')]//*[text()='Passport Id']").getText(), passportId); assertEquals($(“//div[contains(@class,'contacts')]//*[text()='Passport Number']").getText(), passportNumber);
REDUCE AMOUNT OF CODE Ability to fill and submit form of 10 elementsAnd verify that this form filled correctly takes40-45 lines of code 16 ?
REDUCE AMOUNT OF CODE public void submit(String name, String familyName, String passportId, ...., Boolean accept) { this.name.sendKeys(name); this.familyName.sendKeys(familyName); ... if (accept && !this.accept.isSelected() || !accept && this.accept.isSelected()) this.accept.click(); submitButton.click(); }} public class ContactForm { @FindBy(xpath = "//div[contains(@class,'contacts')]//*[text()='Name']") public WebElement name; @FindBy(xpath = "//div[contains(@class,'contacts')]//*[text()='Family Name']") public WebElement familyName; } 17
REDUCE AMOUNT OF CODE public class ContactForm extends Form<Contacts> { @FindBy(xpath = "//div[contains(@class,'contacts')]//*[text()='Name']") public WebElement name; @FindBy(xpath = "//div[contains(@class,'contacts')]//*[text()='Family Name']") public WebElement familyName; } @FindBy(css = “div.contacts")public ContactForm contactForm;public class ContactForm extends Form<Contacts> { @FindBy(xpath = "//*[text()='Name']") public WebElement name; @FindBy(xpath = "//*[text()='Family Name']") public WebElement familyName; } 18
REDUCE AMOUNT OF CODE submitContactForm(“Roman”, “Iovlev”, “123456”, ... , true); contactForm.submit(ROMAN); 19 public class ContactForm extends Form<Contacts> { @XPath("//*[text()='Name']") WebElement name; @XPath("//*[text()='Family Name']") WebElement familyName; } public class ContactForm extends Form<Contacts> { @UI("['Name']") WebElement name; @UI("['Family Name']") WebElement familyName; } public class ContactForm extends Form<Contacts> { UIElement name = $("['Name']"); UIElement familyName = $("['Family Name ']"); … }
REDUCE AMOUNT OF CODE public class ContactForm { @FindBy(css = "//div[contains(@class,'contacts')]//*[text()='Name']") public WebElement name; @FindBy(css = "//div[contains(@class,'contacts')]//*[text()='Family Name']") public WebElement familyName; @FindBy(css = "//div[contains(@class,'contacts')]//*[text()='Passport Id']") public WebElement passId; @FindBy(css = "//div[contains(@class,'contacts')]//*[text()='Passport Number']") public WebElement passNum; ... @FindBy(css = "//div[contains(@class,'contacts')]//*[text()=‘Accept’]") public WebElement accept; @FindBy(css = "//div[contains(@class,'contacts')]//button[text()='Submit']") public WebElement submitButton; public void submit(String name, String familyName, String passportId, String passportNumber, ...., Boolean accept) { this.name.sendKeys(name); this.familyName.sendKeys(familyName); this.passportId.sendKeys(passportId); this.passportNumber.sendKeys(passportNumber); ... if (accept && !this.accept.isSelected() || !accept && this.accept.isSelected()) this.accept.click(); submitButton.click(); }} public class ContactForm extends Form<Contacts> { @UI("['Name']") WebElement name; @UI("['Family Name']") WebElement familyName; @UI("['Passport Id']") WebElement passtId; @UI("['Passport Number']")WebElement passNum; ... @UI("['Name']") WebElement accept; @UI(“['Submit']") WebElement submitButton;} 4X public class ContactForm extends Form<Contacts> { WebElement name, familyName, passtId, passNum, ... accept, submitButton;} 20 40-45 10 2 20X
SMART LOCATORS 21 public class Contacts extends Form<User> { TextField name, lastName, gender, city, address, state; Button submitButton;} public class Contacts extends Form<User> { WebElement name, lastName, gender, city, address, state, submitButton;} public class Contacts extends Form<User> { @Css(“#name”) WebElement name; @Css(“#last-name”) WebElement lastName; @Css(“#gender”) WebElement gender; @Css(“#city”) WebElement city; @Css(“[ui=address]”) WebElement address; @Css(“[ui=state]”) WebElement state; @XPath(“#submit-button”) WebElement submitButton;}
IN ELEMENT ACTIONS new Actions(driver) .moveToElement(myInput) .click().keyDown(Keys.SHIFT) .sendKeys("hello") .build().perform(); myInput.do(a -> a.click().keyDown(Keys.SHIFT) .sendKeys("hello")); new Actions(driver) .clickAndHold().release() .build().perform(); myInput.doActions(a -> a.clickAndHold().release()); 22 myButton.jsExecute(“click()”) ((JavascriptExecutor) driver).executeScript ("arguments[0].click();", myButton); JAVA SCRIPT ACTIONS
ALSO REDUCE CODE Remove all Thread.sleep / waitUntil / waitPageLoad etc.Reduce code for Tables, Dropdowns and other Complex elementsRemove most of logger strings in codeSimplify locatorsDriver Manager: Auto-download driver 23
JDN: UI OBJECTS GENERATOR 2 WRITE TESTS IN MINUTES
JDN 25
JDN DEMO 26 1. Open EpamGithub site https://epam.github.io/JDI2. Click user icon3. Login as User (name: epam, password: 1234)4. Select Contacts page in sidebar menu5. Check that Contacts page opened6. Submit contacts form with:gender = "Male"; religion = "Other"; wheather = "Sun, Snow"; passport = "true"; name = "Roman"; lastName = "Iovlev"; position = "QA Automation"; number = "4321"; seria = "123456"; description = "JDI awesome";7. Check that contact form filled with expected data:gender = "Male"; religion = "Other"; wheather = "Sun, Snow"; passport = "true"; name = "Roman"; lastName = "Iovlev"; position = "QA Automation"; number = "4321"; seria = "123456"; description = "JDI awesome";
GUESS HOW LONG IT WILL TAKE FOR YOU > 8h 4-8 h 2-4 h 1-2 h < 1 h 10-20 min 27
JDN DEMO 28
JDN DEMO 27
BEFORE GENERATION 30
GENERATED PAGE OBJECTS 31
WRITE TESTS IN MINUTES 32 @Testpublic void loginTest() { homePage.open(); header.userIcon.click(); header.loginForm.loginAs(DEFAULT_USER); homePage.checkOpened(); leftMenu.select(ContactForm); contactFormPage.checkOpened(); main.contactForm.submit(DEFAULT_CONTACTS); main.contactForm.check(DEFAULT_CONTACTS);} 1.Generate PageObjects via JDN and place in appropriate folder2.Add test data (DEFAULT_USER, DEFAULT_CONTACTS)3.Write simple test code 15 sec 2-3 minutes 1-2 minutes < 5 mins
RULES 33 Rules <input type=“button” value=“Next”><input type=“button” value=“Previous”><button class=“btn”>Submit</button> "type":"Button","name": “value","css": “input[type=button]“"type":"Button","name": “text","css": “button.btn" @Findby(css=“input[type=button][value=Next]”)public Button next;@Findby(css=“input[type=button][value=Previous]”)public Button previous;@Findby(xpath=“//button[@class=‘btn’ and text()=‘Submit’]”)public Button submit;
BUSINESS LOGS AND REPORTS 3 GET CLEAR AND OBVIOUS RESULTS
LOGS AND REPORTING 35 [ STEP 53:25.314] : Open 'Home Page'(url=https://epam.github.io/JDI/index.html)[ STEP 53:29.616] : Click on 'User Icon'[ STEP 53:29.760] : Login as User(name:epam; password:1234)[ STEP 53:30.307] : Check that 'Home Page' is opened (url CONTAINS '/JDI/index.html'; title EQUALS 'Home Page')[ STEP 53:30.330] : Select '[Contact form]' in 'Left Menu'[ STEP 53:31.176] : Check that 'Contact Form Page' is opened (url CONTAINS '/JDI/contacts.html'; title EQUALS 'Contact Form')[ STEP 53:31.202] : Submit 'Contact Form' with Contacts(gender:Male; weather:Sun, Snow; passport:true; name:Roman; lastName:Iovlev; position:QA Automation; passportNumber:4321; passportSeria:123456; description:JDI awesome)[ STEP 53:33.043] : Check that 'Contact Form' values are: Contacts(gender:Male; weather:Sun, Snow; passport:true; name:Roman; lastName:Iovlev; position:QA Automation; passportNumber:4321; passportSeria:123456; description:JDI awesome) LOGS
LOGS AND REPORTING 36
STABLE AND FAST 4 INCREASE TRUST TO RESULTSREDUCE MAINTENANCE EFFORT
STABLE 38 1. No more false negative 2. No Thread sleeps or other waits Increase trust to your tests and to testing as practiceSaves up to 50-80% of waste engineers effort on bug analysesIncrease tests execution speed Write Less code
FAST TEST EXECUTION 39 1.Textarea input 3000 symbols2.Dropdown 300 values3.Table with 400 rows (4 columns) ? https://epam.github.io/JDI/performance.html - for login use epam/1234
FAST TEXTFIELD AND DROPDOWN JDIPerformanceTests.CS ~96ms 1.Textarea input 3000 symbols ~17 seconds ~250ms 2.Dropdown with 300 values ~12,5 seconds 40 170X 50X
FAST TABLE EXAMPLE usersTable.assertThat().hasRowWithValues( containsValue(“Brock", inColumn("Name")), containsValue(".org", inColumn("Email"))); JDIPerformanceTests.CS 41 https://epam.github.io/JDI/performance.html 400 ~900ms ~5 seconds 5X
JDI IS SIMPLE 5
SIMPLE TO START FROM SCRATCH 1.Simple to setupJust add dependency in your project or use predefined Project Templates2.Simple to learn and write testsNative UI Elements based interfaceWell documented (will be available at April-May 2019)Lot of real examples3.Simple to maintainNative tests, No false negatives, No waits, detailed logs and reporting4.Simple to customizeFlexible to customization on all levels5.Simple to integrateTemplates for integration with popular tools 43
SIMPLE TO IMPROVE SELENIUM PROJECTS 1. Change dependency <dependency> <groupId>com.epam.jdi</groupId> <artifactId>jdi-light</artifactId> <version>RELEASE</version></dependency> dependencies { compile 'com.epam.jdi:jdi-light:+'} 2. Init your page objects with JDI Light PageFactory.initElements(driver, HomePage.class); import static com.epam.jdi.light.ui.html.PageFactory.initElements; 44 See https://github.com/jdi-examlples/jdi-selenium > branch switch-selenium-to-jdi Already have thousands of Selenium tests?
JDI LIGHTHIGHLIGHTS 45
JDI LIGHT HIGHLIGHTS 1.Simple to start and use. Simple to switch from Selenium2.Cool typified elements that native for your application and has a lot of cool unique features out of the box3.Stable and predictable test results4.Good logs and reports out of the box5.Helps to reduce amount of code (LOC) in times6.Increase test’s development speed and test run’s performance 46
START AND INTEGRATION Templates: https://github.com/jdi-templates/ 47 Examples: https://github.com/jdi-examples/ Our projects: https://github.com/jdi-testing/ Hamcrest matchers Powered by SeleniumUse Hamcrest, log4j and AllureEasy integration with Selenium Grid, Selenoid, BrowserStack etc. Documentation: https://jdi-docs.github.io/jdi-light/ (will be available at April-May 2019)
JDI PRODUCTS https://github.com/epam/jdi https://github.com/jdi-testing/jdi-2.0 https://github.com/jdi-testing/jdi-light -JDI original -JDI 2.0 -JDI Light Since 2015 Autumn 2017 Spring 2018 https://github.com/jdi-testing/jdi-http -JDI Dark https://github.com/jdi-testing/jdi-flash-pages -JDN Spring 2018 https://github.com/jdi-testing/jdi-lightsaber -JDI LightSaber Autumn 2017 Since 2016 48
ULTIMATE GOAL 49 You may say I'm a dreamerBut I'm not the only oneI hope someday you'll join usAnd the world will have a fun Imagine there's no, time wastingIt isn't hard to doNo matter what are you testingAnd logs are clear tooImagine all applications tested by AI…You-u-u-u Use AI for PageObjects generation and self-healing; test cases and test data generation; smart tests execution and analyzesIncrease testing tool efficiency and reduce time on routines dramaticallyI hope you’ll join us
Q & A 50 roman.Iovlev roman.Iovlev.jdi@gmail.com http://roman-iovlev.info/ https://join.skype.com/u2Cel0MWHkAO https://jdi-docs.github.io/jdi-light
APPENDIX:MORE COOL FEATURES X
XPATH OR CSS LOCATORS? //*[contains(@class,’user’)]//*[@value=‘Roman’]//div[@name=’product’]//*[contains(class(),‘toast’)]//*[@id=’product’]/*[@type=‘checkbox’]//*[@class=’ice-cream’]//*[@type=‘text’] .user [value=Roman]div[name=product] .toast#product [type=checkbox].ice-cream [type=text] @FindBy(xpath = “…”) @FindBy(css = “…”) BUT… 52
UI LOCATORS //*[contains(@class,’user’)]//*[text()=‘Roman’]//div[@name=’product’]//*[contains(text(),‘Toast’)]//*[@category=’product’][2]//*[text()=’Ice Cream’]/..//*[type=‘checkbox’] .user[‘Roman’]div[name=product][*‘Toast’][category=product][2][’Ice Cream’]<[type=checkbox] @FindBy(xpath = “…”) @UI(“…”) public void selectMenu(String value) { driver.findElement( By.cssSelector(“[value=“ + value + ”]”)).click();}selectMenu(“About”) @Css(“[value=%s]”) WebElement menu;menu.select(“About”) 53
MANAGE WINDOWS getWindows()newWindowIsOpened()setWindowName(String name)windowsCount()switchToNewWindow()openNewTab()originalWindow()switchToWindow(int number)switchToWindow(String name)closeWindow()closeWindow(String name) WindowsManager WindowsAndFramesTests.cs return all WindowHandlesreturn true if new window openedSet specific name for current windowreturn count of opened windowsswitches to latest opened windowopen new windowswitch to first windowswitch to window by numberswitch to window by nameclose current windowclose window by name 54
acceptAlert()declineAlert()getAlertText()sendKeysInAlert(String text)Alert alert() WindowsAndFramesTests.cs accept alertdecline alertget alert textInput text in alert text fieldget alert instance WindowsManager 55 MANAGE ALERTS
UI ELEMENTS UIElement = WebElement jsClick();setText(String value);getText();input(String text);setAttribute(String name, String value);select(String name);setValue(String value);show();hover();scrollUp/Down/Left/Right(int value)higlight() / higlight(String color)find(By) / find(String) / finds(String)printHtml()isDisabled() / isHidden()dragAndDropTo(WebElement) (x, y)doubleClick()rightClick() select() -> new Select(myElement) 56
WEB LIST FILTERS public void HeaderLine { @Css(“#navigation”) public List<WebElement> menu; @Css(“#navigation[item=%s]”) public WebList menu; @Css(“#navigation”) public WebList menu;} WebElement get(String)void select(String/Int)String getValue() Get element by text valueClick on element with text/int valuePrint list values WebList = List<WebElement> 57
WEB LIST FILTERS WebList = List<WebElement> + stream() WebElement first/last()T first/last(JFunc1<T, Boolean> condition)List<T> where/filter(JFunc1<T, Boolean> condition)<R> List<R> select/map(JFunc1<T, R> transform)void ifDo(JFunc1<T, Boolean> condition, JAction1<T> action)List<R> ifSelect(JFunc1<T, Boolean> c, JFunc1<T, R> t)void foreach(JAction1<T> action)boolean any(JFunc1<T, Boolean> condition)boolean all(JFunc1<T, Boolean> condition)List<T> listCopy(int from[, int to])List<R> selectMany(JFunc1<T, List<R>> func) Get first/last elementGet first/last element by conditionFilter elements by conditionTransform all elements using templateDo action in case of condition trueTransform all elements that suit to conditionDo action for all elementsTrue if at least one of elements suit conditionTrue if all elements suit conditionCopy part of listTransform List of list to list 58