Java >> Java opplæring >  >> Java

Veiledning for beste praksis for selen

Denne artikkelen utforsker beste praksis og brukstilfeller for Selenium. For vårt formål vil vi bruke Selenium med Java.

1. Introduksjon

Selen brukes til å automatisere nettlesere. Selenium WebDriver har funnet sin plass i testautomatisering for webapplikasjoner. Den brukes til å lage robuste, nettleserbaserte regresjonsautomatiseringssuiter og tester. Den kan brukes til ende-til-ende-testing av en applikasjon. Dette sikrer at applikasjonen fungerer som den er designet. Selenium støtter ulike programmeringsspråk som C#, Haskell, Java, Javascript, Python og Ruby gjennom Selenium-drivere. Selenium støtter ulike nettlesere som chrome, firefox, safari, opera og internet explorer.

Innholdsfortegnelse

1. Innledning
2. Beste praksis
2.1. Sideobjekt
2.2. Test og kodeseparasjon
2.3. Velgerhierarki
2.4. Vent på
2.5. Atferdsdrevet utvikling
2.6. Datadrevne tester
2.7. Enkelt oppsett
2.8. Alle nettlesere
2.9. Rapportering
3. Sammendrag
4. Last ned kildekoden

2. Selen beste fremgangsmåter

I denne delen vil vi dekke noen av de beste praksisene vi må følge i Selenium-prosjekter. Vi vil dekke hver enkelt i sin underseksjon. Men før vi går inn på det, vil vi se på graderingskonfigurasjonen for prosjektet vårt.

build.gradle
group 'com.jcg'
version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.141.59'
}

group 'com.jcg'
version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

  • Vi har spesifisert et java-prosjekt med gradle byggeverktøy
  • For øyeblikket har prosjektet bare to avhengigheter oppført – en som Junit og andre selenium

Dette gir oss en startpakke for følgende underseksjoner. De nødvendige avhengighetene vil bli dekket under hver underseksjon.

2.1. Sideobjekt

Den første gode praksisen vi ønsker å dekke er Sideobjektmønsteret. Vi vil først dekke et eksempel uten sideobjekt for å diskutere problemene. Vi vil kun dekke testdelen nedenforTestSuite.java

@Test
    public void enterGoogleSearchAndViewResults() {
        webDriver.navigate().to("http://www.google.com");
        WebElement searchText = webDriver.findElement(By.cssSelector("input[title=Search]"));
        searchText.sendKeys("Selenium",Keys.ENTER);
        Assert.assertEquals("Selenium - Google Search", webDriver.getTitle());
    }


    @Test
    public void enterGoogleImageSearch() {
        webDriver.navigate().to("http://www.google.com");
        WebElement searchText = webDriver.findElement(By.cssSelector("input[title=Search]"));
        searchText.sendKeys("Selenium",Keys.ENTER);
        WebElement imageSearch = webDriver.findElement(By.xpath("//a[contains(text(), 'Images')]"));
        imageSearch.click();
    }

Koden ovenfor åpner en Google-hjemmeside og utfører et nettsøk etter selen. Den andre testen ligner på den første testen og åpner til slutt opp bildesøket. Som vist ovenfor har begge testene den repeterende koden, og det er ingen gjenbrukbarhet involvert. Det første trinnet ville være å flytte koden som en funksjon innenfor samme klasse. Selv om det kan være tilstrekkelig for det nåværende eksemplet, er det kanskje ikke skalerbart. Dette kan føre til problemer når vi må endre en enkelt del av skriptet. Alternativt kan vi bruke en Page Object Model . Dette innkapsler elementlokalisatoren og måten å samhandle med elementet i en enkelt klasse. Dette kan gjenbrukes på tvers av ulike testsuiter og fremmer enkeltansvar.POTestSuite.java

@Test
    public void enterGoogleSearchAndViewResults() {
        googleHomePage.openPage();
        googleHomePage.searchText("Selenium", Keys.ENTER);
        Assert.assertEquals("Selenium - Google Search", webDriver.getTitle());
    }


    @Test
    public void enterGoogleImageSearch() {
        googleHomePage.openPage();
        googleHomePage.searchText("Selenium", Keys.ENTER);
        googleResultsPage.performImageSearch();
    }

Dette representerer en mye tynnere test ved å bruke Page Object for å utføre Seleniums lokalisering. Testen handler kun om påstands- eller bekreftelseslogikken.GoogleHomePage.java

public class GoogleHomePage {

    WebDriver webDriver = null;

    public GoogleHomePage(WebDriver webDriver) {
        this.webDriver = webDriver;
    }


    public void openPage() {
        webDriver.navigate().to("http://www.google.com");
    }

    public static By searchInput = By.cssSelector("input[title=Search]");

    public void searchText(CharSequence... keys) {
        webDriver.findElement(searchInput).sendKeys(keys);
    }

}
GoogleResultsPage.java
public class GoogleResultsPage {

    WebDriver webDriver = null;

    public GoogleResultsPage(WebDriver webDriver) {
        this.webDriver = webDriver;
    }


    public void performImageSearch() {
        webDriver.findElement(imageSearch).click();
    }

    public static By imageSearch = By.xpath("//a[contains(text(), 'Images')]");
}

De to ovennevnte kodeblokkene indikerer bruken av Page Object Model . Den inneholder alle lokalisatorene som brukes på siden og gir også gjenbrukbar atferd som søk. Dette vil være til stor hjelp når det må brukes på tvers av flere tester.

2.2. Test og kodeseparasjon

Med tanke på det første eksemplet i forrige seksjon, la vi merke til at selen og testkode var plassert i samme fil under testmappen. Men dette fører til tett kobling mellom testrammeverket (Junit) og automatiseringsramme(Selenium) . Ideelt sett må vi koble dem fra hverandre. Page Object er en måte å separere på. Selv om vi ikke bruker Page Object (selv om jeg anbefaler det på det sterkeste), vi bør skille Selenium logikk fra kjernetestingslogikken til Junit . Dette gir logisk og renere separasjon av kode, noe som gagner programmerere mye.

2.3 Velgerhierarki

Det generelle velgerhierarkiet som skal følges er id > name > css > xpath . Ved hjelp av id kan vi unikt lokalisere et element på nettstedet og kan garantere at elementet identifiseres. Navnesøker etterfølges av id-søker selv om det er mulig, visse elementer kan gjenbruke navnet og vi kan ende opp i konflikter. CSS locator er den anbefalte måten å bruke når id og navn ikke er tilgjengelig. CSS locator brukes til å identifisere et element ved dets attributter og i noen tilfeller dets posisjon i forhold til dets overordnede element(er). Det er en av de mest fleksible lokatorene som finnes. Xpath er også en god lokaliseringsstrategi som fungerer på grunnlag av html dokumentstruktur. Selv om den er svært fleksibel, har den relativt mindre ytelse enn CSS . CSS er naturlig støttet av nettleseren mens Xpath implementering er forskjellig for forskjellige nettlesere, noe som kan føre til inkonsekvens. Vi vil ta en titt på eksempler for hver locator i kodeblokken nedenfor.

    
<html>
<head>
    <title>Hello</title>
</head>
<body>
<button id="click">Click Me</button>
<ol name="list">
    <li>Item1</li>
    <li>Item2</li>
    <li>Item3</li>
</ol>
</body>
</html>

Dette er en enkel HTML-side som inneholder en knapp og en ordnet liste med tre elementer. Vi vil bruke forskjellige locatorer for å identifisere hvert element og tilsvarende hevde teksten til elementet.Selectors.Java(Id)

WebElement clickElement = webDriver.findElement(By.id("click"));
Assert.assertEquals("Click Me", clickElement.getText());

Vi finner knappen ved å bruke id-klikken. Når elementet er lokalisert, bekrefter vi om knappeteksten er klikk MeSelectors.Java(Name)

WebElement listElement = webDriver.findElement(By.name("list"));
Assert.assertTrue(listElement.isDisplayed());

Vi finner den uordnede listen ved å bruke navnet liste . Vi hevder at elementet vises på html-siden i den påfølgende linjenSelectors.Java(CSS)

WebElement listElement = webDriver.findElement(By.cssSelector("ol[name='list']>li"));
Assert.assertEquals(listElement.getText(),"Item1");

Vi finner den uordnede listen ved å bruke navnet liste . Vi spesifiserer så det direkte underordnede li av den uordnede listen. Dette gir oss det første listeelementet hvis tekst vi hevder som Item1.Selectors.Java(XPath)

WebElement listElement = webDriver.findElement(By.xpath("//ol[@name='list']/li[2]"));
 Assert.assertEquals(listElement.getText(),"Item2");

Vi finner den uordnede listen ved å bruke navnet liste . Vi finner så det andre barnet li av den uordnede listen. Dette gir oss listeelementet hvis tekst vi hevder som Item2.

2.4 Vent på

Alle brukeroperasjoner i en nettleser er asynkrone. dvs. vi venter på at brukeren skal utføre en handling. Dette vil også være direkte aktuelt i selen tester. Noen ganger tar en applikasjon litt å laste på grunn av ulike faktorer. Vi vil ikke at Selenium-tester skal mislykkes i løpet av den tiden. Som alltid kan vi ikke ha en uendelig ventetid som igjen fører til dårlig ytelse. I løpet av denne tiden kan vi bruke Thread.Sleep for å få Java-tråden vår til å vente på utførelse. Det anbefales definitivt ikke å bruke fordi det alltid venter på den angitte tiden. Dette resulterer i økt utførelsestid av selen-tester. Alternativt bør vi bruke selen ventetid for å håndtere disse tidkrevende samtalene. Ytterligere ventinger er også klassifisert som implisitt, eksplisitt og flytende venting.Implisitt vent

webDriver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);

Webdriver venter i 10 sekunder før du kaster et element som ikke er funnet unntak i tilfellet ovenfor. Her har vi spesifisert tidsavbruddet implisitt for alle anrop.Eksplisitt vent

WebDriverWait webDriverWait = new WebDriverWait(webDriver,3);
webDriverWait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//ol[@name='list']/li[2]")));

Dette eksemplet spesifiserer eksplisitt tidsavbruddet for en bestemt tilstand. Her har vi spesifisert tidsavbrudd på 3 sekunder eller listeelementet i forrige eksempel har blitt lastet inn. Hvis elementet ikke lastes inn innen 3 sekunder, kaster Selenium et element som ikke ble funnet.Flytende vent

 WebDriverWait webDriverWait = new WebDriverWait(webDriver,10,1000);
webDriverWait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("ol[name='list']>li")));

For å utvide eksemplet ovenfor, kan vi også spesifisere en valgfri avstemningstid for nettdriveren. I dette tilfellet spør webdriver hver 1000 ms, dvs. 1 sekund før elementet på nettsiden blir funnet. Dette reduserer den hyppige pollingen fra webdriverenden.

2.5 Atferdsdrevet utvikling

Selen passer til E2E-testing av en applikasjon og sikrer at systemet fungerer som designet. Aksepttest sikrer at de riktige tingene bygges. Automatisert aksepttesting er et av prinsippene for ekstrem programmering.

Cucumber prøver å ta tak i området for aksepttesting. Agurk tillater samarbeid mellom forretningsinteressenter og utviklingsteam for å uttrykke forretningsresultatene. Agurk har sitt eget allestedsnærværende språk og overholder syntaksregler kjent som Gherkin. BDD anbefales for å sikre at funksjonene blir tydelig kommunisert til både forretningsinteressenter og ingeniører.

I denne delen skal vi se på å konvertere Googles testtilfelle til en agurkfunksjon som tydelig indikerer testintensjonen vår.search.feature

Feature: Search

  Scenario: Successful Search
    Given I open chrome browser
    When I navigate to google search page
    And I provide search text as selenium and enter
    Then Selenium should be in page title

Filen ovenfor er en Cucumber funksjonsfil som tydelig indikerer trinnene tatt av brukeren og feltet som vi ønsker å hevde på. Dette oppnås ved å bruke Cucumber spesifikke søkeord som Given, When, And og Then . Hele testsaken er merket som Scenario mens gruppe av slike testtilfeller utgjør Feature

Dette setter opp en Cucumber Test suite når den kombineres med en Cucumber junit runner. Vi vil ta en titt nedenfor på testfilen som er generert for den tilsvarende funksjonen.
SearchSeleniumSteps.java

public class SearchSeleniumSteps {

    GoogleHomePage googleHomePage;


    @Given("^I open chrome browser$")
    public void iOpenChromeBrowser() throws Throwable {
        googleHomePage = new GoogleHomePage(new ChromeDriver());
    }

    @When("^I navigate to google search page$")
    public void iNavigateToGoogleSearchPage() throws Throwable {
        googleHomePage.openPage();
    }

    @When("^I provide search text as selenium and enter$")
    public void iProvideSearchTextAsSeleniumAndEnter() throws Throwable {
        googleHomePage.searchText("Selenium",Keys.ENTER);
    }

    @Then("^Selenium should be in page title$")
    public void seleniumShouldBeInPageTitle() throws Throwable {
        Assert.assertEquals("Selenium - Google Search", googleHomePage.getWebDriver().getTitle());
        googleHomePage.getWebDriver().quit();
    }
}
  • Hver metode følger trinnet som er definert i funksjonsfilen.
  • Her har vi brukt det sammen med sideobjektmønsteret beskrevet ovenfor.
  • Dette presenterer en tydelig funksjon for testing blant både utviklere og interessenter.

2.6 Datadrevne tester

Vi vil bygge videre på den forrige delen for å bygge datadrevne tester. Vi tester for tiden Google-søk ved å gi et nøkkelord for å søke. Ideelt sett må vi kanskje teste Google-søk med forskjellige søkeord. En måte å gjøre det på er å ha scenarier for hvert søkeordsøk. Dette vil være svært repeterende for selv et par søk. En ideell tilnærming er å ha de nøyaktige trinnene, men gi en måte for oss å legge inn søkeordet alene.search.feature

Scenario Outline: Successful Search
    Given I open chrome browser
    When I navigate to google search page
    And I provide search text as "<searchTerm>" and enter
    Then "<searchTerm>" should be in page title

    Examples:
      | searchTerm |
      | Selenium |
      | Cucumber |

I eksemplet ovenfor, Scenario Outline brukes til å gi en datadrevet test. Parameteren er gitt som searchTerm innenfor vinklede parenteser. Til slutt er dataene for søkeordet vårt gitt under Examples section .

Vi vil se på den tilsvarende testimplementeringen for denne funksjonen i blokken nedenfor
SearchSeleniumSteps.java

public class SearchSeleniumSteps {

  ...

@When("^I provide search text as \"([^\"]*)\" and enter$")
    public void iProvideSearchTextAsAndEnter(String searchTerm) throws Throwable {
        googleHomePage.searchText(searchTerm,Keys.ENTER);
    }

    @Then("^\"([^\"]*)\" should be in page title$")
    public void shouldBeInPageTitle(String searchTerm) throws Throwable {
        Assert.assertEquals(searchTerm+" - Google Search", googleHomePage.getWebDriver().getTitle());
        googleHomePage.getWebDriver().quit();
    }
}
  • Testen godtar et regex-mønster (alle tegn) som input for søkeord som samsvarer med teksten som er skrevet inn.
  • Samme søkeord brukes for påstand i den påfølgende testmetoden.

2.7 Enkelt oppsett

Så mye som Selenium gir oss en konsistent måte å teste på tvers av mange nettlesere, må vi kanskje laste ned de riktige driverne i hver maskin sammen med de nødvendige nettleserne. Feil versjoner av nettlesere kan noen ganger forårsake rare problemer og kan være vanskelig å feilsøke. Den beste tilnærmingen er å containerisere ved hjelp av docker og gi docker-bilder for utviklerne å teste. Vi vil se på et eksempel på docker-filen og se hvordan vi kan utnytte docker-filen i testen vår.docker-compose.yml

  version: "3"
  services:
    selenium-hub:
      image: selenium/hub:3.141.59-bismuth
      container_name: selenium-hub
      ports:
        - "4444:4444"
    chrome:
      image: selenium/node-chrome:3.141.59-bismuth
      depends_on:
        - selenium-hub
      environment:
        - HUB_HOST=selenium-hub
        - HUB_PORT=4444
    firefox:
      image: selenium/node-firefox:3.141.59-bismuth
      depends_on:
        - selenium-hub
      environment:
        - HUB_HOST=selenium-hub
        - HUB_PORT=4444

Filen ovenfor brukes til å bygge et selennett som inneholder firefox-forekomst og chrome-forekomst. Nettleserne er koblet til nettet og den tilsvarende selenhuben er eksponert via port 4444. Docker gir også fordelen med å skalere opp forekomstene i tilfelle parallell utførelse av tester.SearchSeleniumSteps.java

@Given("^I open chrome browser$")
    public void iOpenChromeBrowser() throws Throwable {
        googleHomePage = new GoogleHomePage(new RemoteWebDriver(
                new URL("http://localhost:4444/wd/hub"), new ChromeOptions()));
    }

Den eneste endringen som kreves for å koble til en Docker-basert Selenium-forekomst er å bruke RemoteWebDriver og koble til docker-selenhuben som er eksponert via port 4444.

2.8 Alle nettlesere

Den iboende fleksibiliteten selen gir er at den samme koden kan brukes til å teste på tvers av alle nettlesere. Men det er ikke tilrådelig å teste den samme funksjonaliteten på tvers av alle nettlesere. Vi kan teste kritiske funksjoner på tvers av alle nettlesere, men ikke nødvendig alle funksjonene. Det fører til økt testtid og sjelden kan noen av funksjonene mislykkes. Vi skal se på skalering av testen på tvers av alle nettlesere bortsett fra noen av eksemplene.search.feature

  Scenario Outline: Successful Search
    Given I open "<browser>" browser
    When I navigate to google search page
    And I provide search text as "<searchTerm>" and enter
    Then "<searchTerm>" should be in page title

    Examples:
      | searchTerm|browser  |
      | Selenium  |chrome   |
      | Cucumber  |chrome   |
      | Selenium  |firefox  |

I denne funksjonen har vi spesifisert nettleseren også som konfigurerbar param. Dette indikerer testen for hvert eksempel, hvilken type nettleser som skal brukes. Vi søker etter Selen i begge nettlesere mens agurk søkes kun i chrome. Dette er for å understreke ideen om at ikke alle funksjoner og eksempler trenger å kjøres på tvers av alle tilgjengelige nettlesere.

2.9 Rapportering

I den siste delen vil vi ta en titt på den viktige delen av Selen, dvs. rapportering. Å kjøre Junit-testene indikerer antall beståtte tester i konsollen. Men dette vil ikke være tilgjengelig for senere analyse. Detaljerte rapporter bør genereres for analyse og også identifisere suksessrater.CucumberLauncher.java

  @RunWith(Cucumber.class)
@CucumberOptions(plugin = {"html:target/cucumber-html-report"},
        snippets = SnippetType.CAMELCASE)
public class CucumberLauncher {

}
  • Denne standardstarteren genererer en rapport som inneholder eksempelnivårapporter
  • Dette kan gi en detaljert analyse av testfeilene som vi kan se fra skjermbildet nedenfor.

Ovennevnte indikerer at det har oppstått en feil. Men dette er kanskje ikke nok til å feilsøke. Så i tilfelle feil, er det tilrådelig å ta et skjermbilde av den faktiske siden. Dette kan oppnås ved å bruke koden nedenfor som tar et skjermbilde av nettsiden.CucumberLauncher.java

 public void takeScreenShot(String fileName){
        byte[] bytes=((TakesScreenshot) webDriver).getScreenshotAs(OutputType.BYTES);
        File DestFile=new File(fileName);
        try (FileOutputStream fos = new FileOutputStream(fileName)) {
            try {
                fos.write(bytes);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  • Vi pakker ut WebDriver til typen TakesScreenshot
  • Vi tar et skjermbilde og lagrer det som byte array .
  • Den oppnådde byte array lagres i filen levert av testsaken.

Agurkrapport gir som standard en trinnvis rapport over funksjoner. Dette kan tilfredsstille behovene til utviklere, men kan være utilstrekkelig for et bilde på høyt nivå. Vi kan bruke ExtentReports-plugin sammen med agurk for å gi et dashbord for vår testsuitebuild.gradle

compile group: 'com.aventstack', name: 'extentreports', version: '4.0.7'
    compile group: 'com.vimalselvam', name: 'cucumber-extentsreport', version: '3.1.1'
CucumberLauncher.java
@RunWith(Cucumber.class)
@CucumberOptions(plugin = {"html:target/cucumber-html-report",
        "com.vimalselvam.cucumber.listener.ExtentCucumberFormatter:target/cucumber-reports/report.html"},
        snippets = SnippetType.CAMELCASE)
public class CucumberLauncher {

}

Vi har lagt til gradle-avhengigheten for omfangsrapporter og også den tilsvarende endringen i vår java-klasse for å sikre at plugin-en blir plukket opp for rapportering. Skjermbildet nedenfor viser dashbordet med et klart bilde av feil vs suksesser.

3. Sammendrag

I denne opplæringen har vi dekket de beste praksisene som er involvert i Selenium-testing. Vi dekket sideobjektmønsteret og atferdsdrevet testing. Vi så hvordan Docker kan redusere oppsetttiden for Selenium-tester. Vi tok en titt på hvordan rapporteringslandskapet eksisterer og så også hvordan man strukturerer selentester for vedlikehold. På noen måte er denne listen ikke uttømmende, men den dekker noen av de beste praksisene som definitivt hjelper til med selenutvikling og unngår vanlige fallgruver.

4. Last ned kildekoden

kjerne java selen
Java Tag