Uma nota sobre “Melhores práticas”: evitamos intencionalmente a frase “Melhores
Práticas” nesta documentação. Nenhuma abordagem funciona para todas as situações.
Preferimos a ideia de “Diretrizes e Recomendações”. Nós encorajamos
que você leia e decida cuidadosamente quais abordagens
funcionarão para você em seu ambiente específico.
O teste funcional é difícil de acertar por muitos motivos.
Como se o estado, a complexidade e as dependências do aplicativo não tornassem o teste suficientemente difícil,
lidar com navegadores (especialmente com incompatibilidades entre navegadores)
torna a escrita de bons testes um desafio.
Selenium fornece ferramentas para facilitar a interação funcional do usuário,
mas não o ajuda a escrever suítes de teste bem arquitetadas.
Neste capítulo, oferecemos conselhos, diretrizes e recomendações
sobre como abordar a automação funcional de páginas da web.
Este capítulo registra os padrões de design de software populares
entre muitos dos usuários do Selenium
que tiveram sucesso ao longo dos anos.
1 - Modelos de objetos de página
Note: this page has merged contents from multiple sources, including
the Selenium wiki
Overview
Within your web app’s UI, there are areas where your tests interact with.
A Page Object only models these as objects within the test code.
This reduces the amount of duplicated code and means that if the UI changes,
the fix needs only to be applied in one place.
Page Object Model is a Design Pattern that has become popular in test automation for
enhancing test maintenance and reducing code duplication. A page object is an
object-oriented class that serves as an interface to a page of your AUT. The
tests then use the methods of this page object class whenever they need to
interact with the UI of that page. The benefit is that if the UI changes for
the page, the tests themselves don’t need to change, only the code within the
page object needs to change. Subsequently, all changes to support that new UI
are located in one place.
Advantages
There is a clean separation between the test code and page-specific code, such as
locators (or their use if you’re using a UI Map) and layout.
There is a single repository for the services or operations the page offers
rather than having these services scattered throughout the tests.
In both cases, this allows any modifications required due to UI changes to all
be made in one place. Helpful information on this technique can be found on
numerous blogs as this ‘test design pattern’ is becoming widely used. We
encourage readers who wish to know more to search the internet for blogs
on this subject. Many have written on this design pattern and can provide
helpful tips beyond the scope of this user guide. To get you started,
we’ll illustrate page objects with a simple example.
Examples
First, consider an example, typical of test automation, that does not use a
page object:
/***
* Tests login feature
*/publicclassLogin{publicvoidtestLogin(){// fill login data on sign-in pagedriver.findElement(By.name("user_name")).sendKeys("userName");driver.findElement(By.name("password")).sendKeys("my supersecret password");driver.findElement(By.name("sign-in")).click();// verify h1 tag is "Hello userName" after logindriver.findElement(By.tagName("h1")).isDisplayed();assertThat(driver.findElement(By.tagName("h1")).getText(),is("Hello userName"));}}
There are two problems with this approach.
There is no separation between the test method and the AUT’s locators (IDs in
this example); both are intertwined in a single method. If the AUT’s UI changes
its identifiers, layout, or how a login is input and processed, the test itself
must change.
The ID-locators would be spread in multiple tests, in all tests that had to
use this login page.
Applying the page object model, this example could be rewritten like this
in the following example of a page object for a Sign-in page.
importorg.openqa.selenium.By;importorg.openqa.selenium.WebDriver;/**
* Page Object encapsulates the Sign-in page.
*/publicclassSignInPage{protectedWebDriverdriver;// <input name="user_name" type="text" value="">privateByusernameBy=By.name("user_name");// <input name="password" type="password" value="">privateBypasswordBy=By.name("password");// <input name="sign_in" type="submit" value="SignIn">privateBysigninBy=By.name("sign_in");publicSignInPage(WebDriverdriver){this.driver=driver;if(!driver.getTitle().equals("Sign In Page")){thrownewIllegalStateException("This is not Sign In Page,"+" current page is: "+driver.getCurrentUrl());}}/**
* Login as valid user
*
* @param userName
* @param password
* @return HomePage object
*/publicHomePageloginValidUser(StringuserName,Stringpassword){driver.findElement(usernameBy).sendKeys(userName);driver.findElement(passwordBy).sendKeys(password);driver.findElement(signinBy).click();returnnewHomePage(driver);}}
and page object for a Home page could look like this.
importorg.openqa.selenium.By;importorg.openqa.selenium.WebDriver;/**
* Page Object encapsulates the Home Page
*/publicclassHomePage{protectedWebDriverdriver;// <h1>Hello userName</h1>privateBymessageBy=By.tagName("h1");publicHomePage(WebDriverdriver){this.driver=driver;if(!driver.getTitle().equals("Home Page of logged in user")){thrownewIllegalStateException("This is not Home Page of logged in user,"+" current page is: "+driver.getCurrentUrl());}}/**
* Get message (h1 tag)
*
* @return String message text
*/publicStringgetMessageText(){returndriver.findElement(messageBy).getText();}publicHomePagemanageProfile(){// Page encapsulation to manage profile functionalityreturnnewHomePage(driver);}/* More methods offering the services represented by Home Page
of Logged User. These methods in turn might return more Page Objects
for example click on Compose mail button could return ComposeMail class object */}
So now, the login test would use these two page objects as follows.
There is a lot of flexibility in how the page objects may be designed, but
there are a few basic rules for getting the desired maintainability of your
test code.
Assertions in Page Objects
Page objects themselves should never make verifications or assertions. This is
part of your test and should always be within the test’s code, never in a page
object. The page object will contain the representation of the page, and the
services the page provides via methods but no code related to what is being
tested should be within the page object.
There is one, single, verification which can, and should, be within the page
object and that is to verify that the page, and possibly critical elements on
the page, were loaded correctly. This verification should be done while
instantiating the page object. In the examples above, both the SignInPage and
HomePage constructors check that the expected page is available and ready for
requests from the test.
Page Component Objects
A page object does not necessarily need to represent all the parts of a
page itself. This was noted by Martin Fowler in the early days, while first coining the term “panel objects”.
The same principles used for page objects can be used to
create “Page Component Objects”, as it was later called, that represent discrete chunks of the
page and can be included in page objects. These component objects can
provide references to the elements inside those discrete chunks, and
methods to leverage the functionality or behavior provided by them.
For example, a Products page has multiple products.
<!-- Inventory Item --><divclass="inventory_item"><divclass="inventory_item_name">Backpack</div><divclass="pricebar"><divclass="inventory_item_price">$29.99</div><buttonid="add-to-cart-backpack">Add to cart</button></div></div>
The Products page HAS-A list of products. This object relationship is called Composition. In simpler terms, something is composed of another thing.
publicabstractclassBasePage{protectedWebDriverdriver;publicBasePage(WebDriverdriver){this.driver=driver;}}// Page ObjectpublicclassProductsPageextendsBasePage{publicProductsPage(WebDriverdriver){super(driver);// No assertions, throws an exception if the element is not loadednewWebDriverWait(driver,Duration.ofSeconds(3)).until(d->d.findElement(By.className("header_container")));}// Returning a list of products is a service of the pagepublicList<Product>getProducts(){returndriver.findElements(By.className("inventory_item")).stream().map(e->newProduct(e))// Map WebElement to a product component.toList();}// Return a specific product using a boolean-valued function (predicate)// This is the behavioral Strategy Pattern from GoFpublicProductgetProduct(Predicate<Product>condition){returngetProducts().stream().filter(condition)// Filter by product name or price.findFirst().orElseThrow(()->newRuntimeException("Product not found"));// Error thrown during actual test run}}
The Product component object is used inside the Products page object.
publicabstractclassBaseComponent{protectedWebElementroot;publicBaseComponent(WebElementroot){this.root=root;}}// Page Component ObjectpublicclassProductextendsBaseComponent{// The root element contains the entire componentpublicProduct(WebElementroot){super(root);// inventory_item}publicStringgetName(){// Locating an element begins at the root of the componentreturnroot.findElement(By.className("inventory_item_name")).getText();}publicBigDecimalgetPrice(){returnnewBigDecimal(root.findElement(By.className("inventory_item_price")).getText().replace("$","")).setScale(2,RoundingMode.UNNECESSARY);// Sanitation and formatting}publicvoidaddToCart(){root.findElement(By.id("add-to-cart-backpack")).click();}}
So now, the products test would use the page object and the page component object as follows.
publicclassProductsTest{@TestpublicvoidtestProductInventory(){varproductsPage=newProductsPage(driver);// page objectvarproducts=productsPage.getProducts();assertEquals(6,products.size());// expected, actual}@TestpublicvoidtestProductPrices(){varproductsPage=newProductsPage(driver);// Pass a lambda expression (predicate) to filter the list of products// The predicate or "strategy" is the behavior passed as parametervarbackpack=productsPage.getProduct(p->p.getName().equals("Backpack"));// page component objectvarbikeLight=productsPage.getProduct(p->p.getName().equals("Bike Light"));assertEquals(newBigDecimal("29.99"),backpack.getPrice());assertEquals(newBigDecimal("9.99"),bikeLight.getPrice());}}
The page and component are represented by their own objects. Both objects only have methods for the services they offer, which matches the real-world application as is the core principle of object-oriented programming. When applications are built, they are not made of a massive page entity. They are built with components contained in a page. Page Component Objects implement the same approach.
You can even
nest component objects inside other component objects for more complex
pages. If a page in the AUT has multiple components, or common
components used throughout the site (e.g. a navigation bar), then it
may improve maintainability and reduce code duplication.
Other Design Patterns Used in Testing
There are other design patterns that also may be used in testing. Discussing all of these is
beyond the scope of this user guide. Here, we merely want to introduce the
concepts to make the reader aware of some of the things that can be done. As
was mentioned earlier, many have blogged on this topic and we encourage the
reader to search for blogs on these topics.
Implementation Notes
Page Objects can be thought of as facing in two directions simultaneously. Facing toward the developer of a test, they represent the services offered by a particular page. Facing away from the developer, they should be the only thing that has a deep knowledge of the structure of the HTML of a page (or part of a page) It’s simplest to think of the methods on a Page Object as offering the “services” that a page offers rather than exposing the details and mechanics of the page. As an example, think of the inbox of any web-based email system. Amongst the services it offers are the ability to compose a new email, choose to read a single email, and list the subject lines of the emails in the inbox. How these are implemented shouldn’t matter to the test.
Because we’re encouraging the developer of a test to try and think about the services they’re interacting with rather than the implementation, Page Objects should seldom expose the underlying WebDriver instance. To facilitate this, methods on the Page Object may return another Page Object, another Page Component Object, or even itself (this). This means we can effectively model the user’s journey through our application. It also means that should the way that pages relate to one another change (like when the login page asks the user to change their password the first time they log into a service when it previously didn’t do that), simply changing the appropriate method’s signature will cause the tests to fail to compile. Put another way; we can tell which tests would fail without needing to run them when we change the relationship between pages and reflect this in the Page Objects.
One consequence of this approach is that it may be necessary to model (for example) both a successful and unsuccessful login; or a click could have a different result depending on the app’s state. When this happens, it is common to have multiple methods on the Page Object:
publicclassLoginPage{publicHomePageloginAs(Stringusername,Stringpassword){// ... clever magic happens here}publicLoginPageloginAsExpectingError(Stringusername,Stringpassword){// ... failed login here, maybe because one or both of the username and password are wrong}publicStringgetErrorMessage(){// So we can verify that the correct error is shown}}
The code presented above shows an important point: the tests, not the Page Objects, should be responsible for making assertions about the state of a page. For example:
publicvoidtestMessagesAreReadOrUnread(){Inboxinbox=newInbox(driver);inbox.assertMessageWithSubjectIsUnread("I like cheese");inbox.assertMessageWithSubjectIsNotUnread("I'm not fond of tofu");}
could be re-written as:
publicvoidtestMessagesAreReadOrUnread(){Inboxinbox=newInbox(driver);assertTrue(inbox.isMessageWithSubjectIsUnread("I like cheese"));assertFalse(inbox.isMessageWithSubjectIsUnread("I'm not fond of tofu"));}
Of course, as with every guideline, there are exceptions, and one that is commonly seen with Page Objects is to check that the WebDriver is on the correct page when we instantiate the Page Object. This is done in the example below.
Finally, a Page Object need not represent an entire page and can be composed of Page Object Components. These components may represent a section that appears frequently within a site or page, such as site navigation. The essential principle is that there is only one place in your test suite with knowledge of the structure of the HTML of a particular (part of a) page.
Summary
The public methods represent the services that the page or component offers
Try not to expose the internals of the page or component
Generally don’t make assertions
Methods return other Page Objects, Page Component Objects, or optionally themselves (for fluent syntax)
Need not represent an entire page all the time
Different results for the same action are modelled as different methods
Example
publicclassLoginPage{privatefinalWebDriverdriver;publicLoginPage(WebDriverdriver){this.driver=driver;// Check that we're on the right page.if(!"Login".equals(driver.getTitle())){// Alternatively, we could navigate to the login page, perhaps logging out firstthrownewIllegalStateException("This is not the login page");}}// The login page contains several HTML elements that will be represented as WebElements.// The locators for these elements should only be defined once.ByusernameLocator=By.id("username");BypasswordLocator=By.id("passwd");ByloginButtonLocator=By.id("login");// The login page allows the user to type their username into the username fieldpublicLoginPagetypeUsername(Stringusername){// This is the only place that "knows" how to enter a usernamedriver.findElement(usernameLocator).sendKeys(username);// Return the current page object as this action doesn't navigate to a page represented by another PageObjectreturnthis;}// The login page allows the user to type their password into the password fieldpublicLoginPagetypePassword(Stringpassword){// This is the only place that "knows" how to enter a passworddriver.findElement(passwordLocator).sendKeys(password);// Return the current page object as this action doesn't navigate to a page represented by another PageObjectreturnthis;}// The login page allows the user to submit the login formpublicHomePagesubmitLogin(){// This is the only place that submits the login form and expects the destination to be the home page.// A separate method should be created for the instance of clicking login whilst expecting a login failure. driver.findElement(loginButtonLocator).submit();// Return a new page object representing the destination. Should the login page ever// go somewhere else (for example, a legal disclaimer) then changing the method signature// for this method will mean that all tests that rely on this behaviour won't compile.returnnewHomePage(driver);}// The login page allows the user to submit the login form knowing that an invalid username and / or password were enteredpublicLoginPagesubmitLoginExpectingFailure(){// This is the only place that submits the login form and expects the destination to be the login page due to login failure.driver.findElement(loginButtonLocator).submit();// Return a new page object representing the destination. Should the user ever be navigated to the home page after submitting a login with credentials // expected to fail login, the script will fail when it attempts to instantiate the LoginPage PageObject.returnnewLoginPage(driver);}// Conceptually, the login page offers the user the service of being able to "log into"// the application using a user name and password. publicHomePageloginAs(Stringusername,Stringpassword){// The PageObject methods that enter username, password & submit login have already defined and should not be repeated here.typeUsername(username);typePassword(password);returnsubmitLogin();}}
2 - Linguagem específica de domínio (DSL)
Uma linguagem específica de domínio (DSL) é um sistema que fornece ao usuário
um meio expressivo de resolver um problema. Ele permite a um usuário
interagir com o sistema em seus termos - não apenas na linguagem do programador.
Seus usuários, em geral, não se importam com a aparência do seu site. Eles não
preocupam-se com a decoração, animações ou gráficos. Eles
deseja usar seu sistema para empurrar seus novos funcionários através do
processo com dificuldade mínima; eles querem reservar uma viagem para o Alasca;
eles querem configurar e comprar unicórnios com desconto. Seu trabalho como
testador deve chegar o mais perto possível de “capturar” essa mentalidade.
Com isso em mente, começamos a “modelar” o aplicativo que você está
trabalhando, de modo que os scripts de teste (o único proxy de pré-lançamento do usuário) “fala a linguagem” e representa o usuário.
Com Selenium, DSL é geralmente representado por métodos, escritos para fazer
a API simples e legível - eles permitem um relatório entre o
desenvolvedores e as partes interessadas (usuários, proprietários de produtos, negócios
especialistas em inteligência, etc.).
Benefícios
Legível: As partes interessadas da empresa podem entendê-lo.
Gravável: Fácil de escrever, evita duplicações desnecessárias.
Extensível: Funcionalidade pode (razoavelmente) ser adicionada
sem quebrar contratos e funcionalidades existentes.
Manutenção: Deixando os detalhes de implementação fora do teste
casos, você está bem isolado contra alterações no AUT *.
Java
Aqui está um exemplo de um método DSL razoável em Java.
Por questão de brevidade, ele assume que o objeto driver é pré-definido
e está disponível para o método.
/**
* Takes a username and password, fills out the fields, and clicks "login".
*
* @return An instance of the AccountPage
*/publicAccountPageloginAsUser(Stringusername,Stringpassword){WebElementloginField=driver.findElement(By.id("loginField"));loginField.clear();loginField.sendKeys(username);// Fill out the password field. The locator we're using is "By.id", and we should// have it defined elsewhere in the class.WebElementpasswordField=driver.findElement(By.id("password"));passwordField.clear();passwordField.sendKeys(password);// Click the login button, which happens to have the id "submit".driver.findElement(By.id("submit")).click();// Create and return a new instance of the AccountPage (via the built-in Selenium// PageFactory).returnPageFactory.initElements(driver,AccountPage.class);}
packagedev.selenium.test_practices;importorg.junit.jupiter.api.Disabled;importorg.junit.jupiter.api.Test;importorg.openqa.selenium.By;importorg.openqa.selenium.WebDriver;importorg.openqa.selenium.WebElement;importorg.openqa.selenium.chrome.ChromeDriver;importorg.openqa.selenium.support.PageFactory;classAccountPage{}classLoginPage{privatefinalWebDriverdriver;publicLoginPage(WebDriverdriver){this.driver=driver;}/**
* Takes a username and password, fills out the fields, and clicks "login".
*
* @return An instance of the AccountPage
*/publicAccountPageloginAsUser(Stringusername,Stringpassword){WebElementloginField=driver.findElement(By.id("loginField"));loginField.clear();loginField.sendKeys(username);// Fill out the password field. The locator we're using is "By.id", and we should// have it defined elsewhere in the class.WebElementpasswordField=driver.findElement(By.id("password"));passwordField.clear();passwordField.sendKeys(password);// Click the login button, which happens to have the id "submit".driver.findElement(By.id("submit")).click();// Create and return a new instance of the AccountPage (via the built-in Selenium// PageFactory).returnPageFactory.initElements(driver,AccountPage.class);}}publicclassDomainSpecificLanguageExample{@Test@Disabled("Illustrative only: LoginPage targets a fictional application, not a live site")voidloginAsUserAbstractsTheLoginFormFromTheTest(){WebDriverdriver=newChromeDriver();try{LoginPageloginPage=newLoginPage(driver);loginPage.loginAsUser("cbrown","cl0wn3");}finally{driver.quit();}}}
Este método abstrai completamente os conceitos de campos de entrada,
botões, cliques e até páginas do seu código de teste. Usando este
abordagem, tudo o que o testador precisa fazer é chamar esse método. Isto dá
uma vantagem de manutenção: se os campos de login mudaram, você
teria apenas que alterar esse método - não seus testes.
publicvoidloginTest(){loginAsUser("cbrown","cl0wn3");// Agora que estamos logados, fazemos alguma outra coisa--como usamos uma DSL para suportar// nossos testadores, é apenas escolher um dos métodos disponíveis.do.something();do.somethingElse();Assert.assertTrue("Algo deveria ter sido feito!",something.wasDone());// Note que ainda não nos referimos a nenhum botão ou web control nesse// script...}
Vale a pena repetir: um de seus principais objetivos deve ser escrever um
API que permite que seus testes resolvam o problema em questão, e NÃO
o problema da IU. A IU é uma preocupação secundária para o seu
usuários - eles não se importam com a interface do usuário, eles apenas querem fazer seu trabalho
feito. Seus scripts de teste devem ser lidos como uma lista de itens sujos que o usuário deseja FAZER e as coisas que deseja SABER. Os testes
não devem se preocupar com COMO a interface do usuário exige que você vá
sobre isso.
*AUT: Application under test
3 - Gerando estado da aplicação
Selenium não deve ser usado para preparar um caso de teste. Tudo as
ações repetitivas e preparações para um caso de teste devem ser feitas por meio de outros
métodos. Por exemplo, a maioria das IUs da web tem autenticação (por exemplo, um formulário de login). Eliminar o login via navegador da web antes de cada teste irá
melhorar a velocidade e estabilidade do teste. Um método deve ser
criado para obter acesso à AUT* (por exemplo, usando uma API para fazer login e definir um
cookie). Além disso, a criação de métodos para pré-carregar dados para
o teste não deve ser feito usando Selenium. Como dito anteriormente,
APIs existentes devem ser aproveitadas para criar dados para a AUT *.
*AUT: Application under test
4 - Simulação de serviços externos
Eliminar as dependências de serviços externos melhorará muito
a velocidade e estabilidade de seus testes.
5 - Relatórios melhorados
O Selenium não foi projetado para relatar sobre o status de casos de teste. Aproveitar
os recursos de relatórios integrados de frameworks de teste unitários é um bom começo.
A maioria dos frameworks de teste unitários podem gerar relatórios formatados em xUnit ou HTML.
Relatórios xUnit são populares para importar resultados para um servidor de integração contínua
(CI) como Jenkins, Travis, Bamboo, etc. Aqui estão alguns links
para obter mais informações sobre resultados de relatórios em vários idiomas.
Embora mencionado em vários lugares, vale a pena mencionar novamente. Garanta que
os testes são isolados uns dos outros.
Não compartilhe dados de teste. Imagine vários testes em que cada um consulta o banco de dados
para pedidos válidos antes de escolher um para executar uma ação. Caso dois testes
peguem a mesma ordem, provavelmente você obterá um comportamento inesperado.
Limpe dados desatualizados no aplicativo que podem ser obtidos por outro
teste, por exemplo registros de pedidos inválidos.
Crie uma nova instância do WebDriver por teste. Isso ajuda a garantir o isolamento do teste
e torna a paralelização mais simples.
If you choose pytest as your test runner, this can be
easily done by yielding your driver in a global fixture. This way each test gets its own
driver instance, and you can ensure that drivers always quit after a test is finished
(pass or fail).
7 - Tips on working with locators
When to use which locators and how best to manage them in your code.
No geral, se os IDs de HTML estiverem disponíveis, únicos e consistentemente
previsíveis, eles são o método preferido para localizar um elemento
uma página. Eles tendem a trabalhar muito rapidamente e dispensar muito processamento
que vem com travessias de DOM complicadas.
Se IDs exclusivos não estiverem disponíveis, um seletor CSS bem escrito é o
método preferido de localização de um elemento. XPath funciona bem como CSS
seletores, mas a sintaxe é complicada e frequentemente difícil de
depurar. Embora os seletores XPath sejam muito flexíveis, eles não são tipicamente testados em performance por fornecedores de navegadores e tendem a ser bastante lentos.
As estratégias de seleção baseadas em linkText e partialLinkText têm
desvantagens porque eles só funcionam em elementos de link. Além disso, eles
chamam seletores querySelectorAll internamente no WebDriver.
O nome da tag pode ser uma maneira perigosa de localizar elementos. tem
frequentemente, vários elementos da mesma tag presentes na página.
Isso é útil principalmente ao chamar o método _findElements(By) _ que
retorna uma coleção de elementos.
A recomendação é manter seus localizadores compactos e
legíveis quanto possível. Pedir ao WebDriver para percorrer a estrutura DOM
é uma operação cara, e quanto mais você pode restringir o escopo de
sua pesquisa, melhor.
8 - Independência de Testes
Escreva cada teste como sua própria unidade. Escreva os testes de uma forma que não seja
dependente de outros testes para concluir:
Digamos que existe um sistema de gerenciamento de conteúdo com o qual você pode criar
algum conteúdo personalizado que então aparece em seu site como um módulo após
publicação, e pode levar algum tempo para sincronizar entre o CMS e a aplicação.
Uma maneira errada de testar seu módulo é que o conteúdo seja criado e
publicado em um teste e, em seguida, verificar o módulo em outro teste. Este teste
não é viável, pois o conteúdo pode não estar disponível imediatamente para o
outro teste após a publicação.
Em vez disso, você pode criar um conteúdo stub que pode ser ligado e desligado
dentro do teste e use-o para validar o módulo. Contudo,
para a criação de conteúdo, você ainda pode ter um teste separado.
9 - Considere usar uma API fluente
Martin Fowler cunhou o termo “API Fluent”. Selenium já
implementa algo assim em sua classe FluentWait, que é
pretende ser uma alternativa à classe padrão Wait.
Você pode habilitar o padrão de design de API fluente em seu objeto de página
e, em seguida, consulte a página de pesquisa do Google com um snippet de código como este:
packagedev.selenium.test_practices;importjava.time.Duration;importorg.junit.jupiter.api.Disabled;importorg.junit.jupiter.api.Test;importorg.openqa.selenium.By;importorg.openqa.selenium.WebDriver;importorg.openqa.selenium.chrome.ChromeDriver;importorg.openqa.selenium.support.ui.WebDriverWait;abstractclassBasePage{protectedWebDriverdriver;publicBasePage(WebDriverdriver){this.driver=driver;}}classGoogleSearchPageextendsBasePage{publicGoogleSearchPage(WebDriverdriver){super(driver);// Generally do not assert within pages or components.// Effectively throws an exception if the lambda condition is not met.newWebDriverWait(driver,Duration.ofSeconds(3)).until(d->d.findElement(By.id("logo")));}publicGoogleSearchPagesetSearchString(Stringsstr){driver.findElement(By.id("gbqfq")).sendKeys(sstr);returnthis;}publicvoidclickSearchButton(){driver.findElement(By.id("gbqfb")).click();}}publicclassFluentApiExample{@Test@Disabled("Illustrative only: targets Google's live, frequently changing search page markup")voidqueryTheGoogleSearchPageUsingAFluentApi(){WebDriverdriver=newChromeDriver();try{driver.get("http://www.google.com/webhp?hl=en&tab=ww");GoogleSearchPagegsp=newGoogleSearchPage(driver);gsp.setSearchString("cheese").clickSearchButton();}finally{driver.quit();}}}
A classe de objeto da página do Google com este comportamento fluente
pode ser assim:
abstractclassBasePage{protectedWebDriverdriver;publicBasePage(WebDriverdriver){this.driver=driver;}}classGoogleSearchPageextendsBasePage{publicGoogleSearchPage(WebDriverdriver){super(driver);// Generally do not assert within pages or components.// Effectively throws an exception if the lambda condition is not met.newWebDriverWait(driver,Duration.ofSeconds(3)).until(d->d.findElement(By.id("logo")));}publicGoogleSearchPagesetSearchString(Stringsstr){driver.findElement(By.id("gbqfq")).sendKeys(sstr);returnthis;}publicvoidclickSearchButton(){driver.findElement(By.id("gbqfb")).click();}}
packagedev.selenium.test_practices;importjava.time.Duration;importorg.junit.jupiter.api.Disabled;importorg.junit.jupiter.api.Test;importorg.openqa.selenium.By;importorg.openqa.selenium.WebDriver;importorg.openqa.selenium.chrome.ChromeDriver;importorg.openqa.selenium.support.ui.WebDriverWait;abstractclassBasePage{protectedWebDriverdriver;publicBasePage(WebDriverdriver){this.driver=driver;}}classGoogleSearchPageextendsBasePage{publicGoogleSearchPage(WebDriverdriver){super(driver);// Generally do not assert within pages or components.// Effectively throws an exception if the lambda condition is not met.newWebDriverWait(driver,Duration.ofSeconds(3)).until(d->d.findElement(By.id("logo")));}publicGoogleSearchPagesetSearchString(Stringsstr){driver.findElement(By.id("gbqfq")).sendKeys(sstr);returnthis;}publicvoidclickSearchButton(){driver.findElement(By.id("gbqfb")).click();}}publicclassFluentApiExample{@Test@Disabled("Illustrative only: targets Google's live, frequently changing search page markup")voidqueryTheGoogleSearchPageUsingAFluentApi(){WebDriverdriver=newChromeDriver();try{driver.get("http://www.google.com/webhp?hl=en&tab=ww");GoogleSearchPagegsp=newGoogleSearchPage(driver);gsp.setSearchString("cheese").clickSearchButton();}finally{driver.quit();}}}
10 - Navegador novo por teste
Comece cada teste a partir de um estado limpo conhecido.
Idealmente, ligue uma nova máquina virtual para cada teste.
Se ligar uma nova máquina virtual não for prático,
pelo menos inicie um novo WebDriver para cada teste.
Most browser drivers like GeckoDriver and ChromeDriver will start with a clean
known state with a new user profile, by default.