Java >> Java tutorial >  >> Java

Selen-webapplikationstesteksempel

1. Introduktion

I dette indlæg viser vi et omfattende eksempel på Selenium-webapplikationstest. En webapplikation er et klient-server-computerprogram, som klienten kører i en webbrowser. Spring Web model-view-controller (MVC) framework giver en nemmere måde at udvikle en webapplikation på. Fjederstøvle giver automatisk konfiguration for at forenkle konfigurationstrinnene. Selenium er et sæt softwareværktøjer, som inkluderer WebDriver (fjernbetjening), Grid, IDE og Client API til at understøtte testautomatisering. Selenium WebDriver accepterer kommandoer og sender dem til en browser. Ifølge Selenium-dokumentationen understøtter den ni populære browsere:Google Chrome, Internet Explorer, Firefox, Safari, Opera, HtmlUnit, phantomjs, Android og iOS.

I dette eksempel vil jeg bygge et web applikation med Spring boot MVC framework. Denne webapplikation vil indeholde:

  1. En landingsside til at vise serverens aktuelle tid og en login-knap.
  2. En brugerdetaljeside for at vise brugerens detaljerede oplysninger, når brugernavnet matches med en eksisterende registrering.
  3. At oprette en ny brugerside for at oprette en ny bruger, når brugernavnet ikke matcher nogen eksisterende registrering.
  4. En fejlside til at udlæse enhver uventet undtagelse.

Jeg vil også bygge et automatiseringstestprojekt med Selenium for at teste denne webapplikation med Google Chrome. Testcaserne omfatter:

  • Test webapplikationens landingsside
  • Test, at webapplikationens login-knap er klikbar
  • Test, at loginsidens brugernavn kan redigeres
  • Test, at der kan oprettes en ny bruger
  • Test, at en eksisterende bruger kan logge ind for at se detaljerne
  • Test, at en fejlside udløses, når der opstår en uventet fejl

2. Anvendte teknologier

Eksempelkoden i denne artikel blev bygget og kørt ved hjælp af:

  • Java 1.8.101
  • Maven 3.3.9
  • Eclipse Oxygen
  • Spring Boot/Web/Data/JPA 1.5.14.RELEASE
  • Selen 2.41.0
  • H2 1.4.197

3. Projektlayout

I dette eksempel vil jeg oprette to Maven-projekter:spring-boot-web-jsp-demo at udvikle en webapplikation og automation-demo for at teste webapplikationen.

Figur 1 Webapplikation

Figur 2 Test automatisering

4. Webapplikationsprojekt

I dette trin vil jeg oprette et Maven-projekt – spring-boot-web-jsp-demo som en Spring boot-webapplikation.

4.1 Afhængigheder

Jeg vil inkludere bibliotekerne i pom.xml .

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<artifactId>spring-boot-web-jsp-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<name>spring-boot-web-jsp-demo</name>
	<description>Demo project for Spring Boot Web App</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.14.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	
		<!-- JSTL -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
		</dependency>

		<!-- Need this to compile JSP for Embedded Tomcat -->
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
		</dependency>

		<!-- Spring MVC -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
		</dependency>

	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

4.2 Egenskaber

Spring Boot-egenskabsfilen er opdateret til at inkludere JSP ViewResolver præfiks og suffiks indstilling.

application.properties

# Spring MVC view prefix.
spring.mvc.view.prefix=/WEB-INF/jsp/

# Spring MVC view suffix.
spring.mvc.view.suffix=.jsp

# Locations of static resources.
spring.resources.staticLocations=/resources/static/

#disable the white label when using an embedded servlet container.
server.error.whitelabel.enabled=false

spring.mvc.throw-exception-if-no-handler-found=true

4.3 Visninger

I MVC-rammen er en visning ansvarlig for at gengive modeldataene for at generere HTML-outputtet, så en browser kan fortolke det. Denne webapplikation har fire visninger. Jeg vil bygge dem med Java Server Page (JSP) og bruge Spring boot-egenskaber til at definere dets præfiks og suffiks.

Selenium web driver API kan nemt finde webelementerne baseret på deres ID'er, så jeg vil indstille webelementernes ID-værdi.

4.3.1 Hjem

Webapplikationens landingsside er home.jsp . Det viser serverens tid og gør det muligt for en bruger at logge ind med et brugernavn.

home.jsp

<!DOCTYPE html>

<html>
<head>
<title>home</title>
</head>
<body>
	<h1>Spring Boot Web JSP Example</h1>
	<h3>The server time is ${serverTime}.</h3>
	<form action="user" method="post">
		User Name : <input type="text" name="userName" id="username">
		<input type="submit" value="Login" id="login">
	</form>
</body>
</html>

Bemærk :Formularhandlingen er knyttet til UserController 's user metode via @RequestMapping(value="/user") .

4.3.2 Ny bruger

Den nye brugerside er newuser.jsp . En bruger indtaster fornavn, efternavn og brugernavn og klikker på Save knappen for at gemme en ny bruger.

newuser.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ page session="false"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>New User</title>
</head>
<body>
	<h2>User ${userName} is not found in the system. Create it!</h2>

	<form:form action="user/save" method="post" modelAttribute="account">
		<p>
			<input type="submit" value="Save" id="save">
		</p>
		<table>
			<tr>
				<td>User Name :</td>
				<td><input type="text" name="username" id="username"
					value="${userName}"></td>
			</tr>
			<tr>
				<td>First Name :</td>
				<td><input type="text" name="firstname" id="firstname"></td>
			</tr>
			<tr>
				<td>Last Name :</td>
				<td><input type="text" name="lasttname" id="lasttname"></td>
			</tr>
		</table>
	</form:form>
</body>
</html>

4.3.3 Brugerdetaljer

user.jsp visningen viser brugerens detaljerede oplysninger.

user.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<spring:url value="/css/main.css" var="demoCSS" />

<link href="${demoCSS}" rel="stylesheet">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>User Account Detail</title>
</head>
<body>
	<h3>Hi ${account.firstname} ${account.lastname}!</h3>

	<p>Account Id: ${account.id} Reward Point:${account.rewardPoint}</p>

	<h3>Transaction Detail</h3>

	<table>
		<tr>
			<th>Date</th>
			<th>Payee</th>
			<th>Description</th>
			<th>Amount</th>
		</tr>
		<c:forEach var="listValue" items="${account.transactions}">
			<tr>
				<td>${listValue.createdDate}</td>
				<td>${listValue.payeeName}</td>
				<td>${listValue.description}</td>
				<td>${listValue.amount}</td>
			</tr>
		</c:forEach>
	</table>

</body>
</html>

4.3.4 Fejl

error.jsp visningen viser enhver uventet undtagelse.

error.jsp

<!DOCTYPE html>

<html>
<head>
<title>error</title>
</head>
<body>
	<h1>Caught Unexpected Exception</h1>
	<h2>from: ${content}.</h2>
	<p>due to: ${error}.</p>
</body>
</html>

4.4 Controllere

I MVC-rammen behandler en controller brugerens anmodning og bygger den passende model og sender den til visningen til gengivelse.

4.4.1 HomeController

HomeController knytter rodkonteksten til welcome metode og returnerer hjemvisningen. Den kortlægger også eventuelle undtagelser til fejlvisningen.

HomeController.java

package jcg.zheng.demo.spring.controller;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HomeController {
	private static final String ERROR_VIEW = "error";

	private static final String HOME_VIEW = "home";

	@RequestMapping("/")
	public String welcome(Locale locale, Map model) {
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

		String formattedDate = dateFormat.format(date);

		model.put("serverTime", formattedDate);
		return HOME_VIEW;
	}

	@ExceptionHandler(Exception.class)
	public String exceptionHandler(HttpServletRequest request, Exception ex, Model model) {
		model.addAttribute("content", request.getRequestURL());
		model.addAttribute(ERROR_VIEW, ex.getMessage());
		return ERROR_VIEW;
	}

}

4.4.2 UserController

UserController kort "/user" til user metode. Det kortlægger også "/user/save" til saveUser metode.

UserController.java

package jcg.zheng.demo.spring.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import jcg.zheng.demo.spring.entity.Account;
import jcg.zheng.demo.spring.model.User;
import jcg.zheng.demo.spring.service.AccountService;

@Controller
public class UserController {

	private static final String NEWUSER_VIEW = "newuser";
	private static final String USER_VIEW = "user";

	@Autowired
	private AccountService accountService;

	@RequestMapping(value = "/user/save", method = RequestMethod.POST)
	public String saveuser(@Validated @ModelAttribute("account") Account acct, Model model) {
		accountService.save(acct);
		model.addAttribute("account", acct);
		return USER_VIEW;

	}

	@RequestMapping(value = "/user", method = RequestMethod.POST)
	public String user(@Validated User user, Model model) {
		model.addAttribute("userName", user.getUserName());

		Account foundUser = accountService.findByUsername(user.getUserName());
		if (foundUser != null) {
			model.addAttribute("account", foundUser);
			return USER_VIEW;
		} else {
			return NEWUSER_VIEW;
		}
	}

}

4.5-model

I MVC-rammen er en model applikationens data.

4.5.1 Bruger

User klasse indeholder username og account objekt.

Bruger.java

package jcg.zheng.demo.spring.model;

import jcg.zheng.demo.spring.entity.Account;

public class User {

	private String userName;

	private Account account;

	public Account getAccount() {
		return account;
	}

	public void setAccount(Account account) {
		this.account = account;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

}

4.5.2 Konto

Account klasse indeholder et unikt ID, fornavn, efternavn, brugernavn og liste over transaktioner.

Account.java

package jcg.zheng.demo.spring.entity;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "ACCOUNT")
public class Account {

	@Id
	@GeneratedValue
	@Column(name = "ID")
	private Integer id;
	
	@Column(name = "First_Name")
	private String firstname;
	
	@Column(name = "Last_Name")
	private String lastname;
	
	private String username;

	@OneToMany(mappedBy="account", cascade = CascadeType.ALL, fetch = FetchType.EAGER, targetEntity = RewardTransaction.class)
	private List transactions = new ArrayList();

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getFirstname() {
		return firstname;
	}

	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}

	public String getLastname() {
		return lastname;
	}

	public void setLastname(String lastname) {
		this.lastname = lastname;
	}

	public BigDecimal getRewardPoint() {
		return transactions.stream().map(RewardTransaction::getAmount)
        .reduce(BigDecimal.ZERO, BigDecimal::add);  
		 
	}

	public List getTransactions() {
		return transactions;
	}

	public void addTransactions(RewardTransaction transaction) {
		transaction.setAccount(this);
		this.transactions.add(transaction);
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

}

4.5.3 Belønningstransaktion

RewardTransaction klasse indeholder et unikt ID, oprettelsesdato, transaktionsbeløb, beskrivelse og betalingsmodtagernavn.

RewardTransaction.java

package jcg.zheng.demo.spring.entity;

import java.math.BigDecimal;
import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

@Entity
public class RewardTransaction {

	@Id
	@GeneratedValue
	private Long id;
	private Date createdDate;
	private BigDecimal amount;
	private String description;
	private String payeeName;

	@ManyToOne
	@JoinColumn(name = "ACCOUNT_ID")
	private Account account;

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getPayeeName() {
		return payeeName;
	}

	public void setPayeeName(String payeeName) {
		this.payeeName = payeeName;
	}

	public Date getCreatedDate() {
		return createdDate;
	}

	public void setCreatedDate(Date createdDate) {
		this.createdDate = createdDate;
	}

	public BigDecimal getAmount() {
		return amount;
	}

	public void setAmount(BigDecimal amount) {
		this.amount = amount;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public Account getAccount() {
		return account;
	}

	public void setAccount(Account account) {
		this.account = account;
	}

}

4.6 Tjenester

AccountService indeholder to metoder:findByUserName og save .

AccountServiceImpl.java

package jcg.zheng.demo.spring.service.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Random;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import jcg.zheng.demo.spring.entity.Account;
import jcg.zheng.demo.spring.entity.RewardTransaction;
import jcg.zheng.demo.spring.repository.AccountRepository;
import jcg.zheng.demo.spring.service.AccountService;

@Service
public class AccountServiceImpl implements AccountService {

	@Autowired
	private AccountRepository acctDao;

	@Override
	public Account findByUsername(String username) {
		return acctDao.findByUserName(username);
	}

	@Override
	public Account save(Account accout) {
		Random rand = new Random();
		accout.addTransactions(createTransaction("Shop&Save", "Food items", new BigDecimal(rand.nextInt(100))));
		accout.addTransactions(createTransaction("Webster", "School supplies", new BigDecimal(rand.nextInt(260))));
		accout.addTransactions(createTransaction("KOHL", "Birthday gifts", new BigDecimal(rand.nextInt(300))));
		accout.addTransactions(createTransaction("Macy", "Allen clothes", new BigDecimal(rand.nextInt(100))));
		accout.addTransactions(createTransaction("Home Depot", "Household items", new BigDecimal(rand.nextInt(1000))));
		accout.addTransactions(createTransaction("Wal-mart", "Small items", new BigDecimal(rand.nextInt(60))));
	
		return acctDao.save(accout);
	}
	
	private RewardTransaction createTransaction(String payee, String desp, BigDecimal amount) {
		RewardTransaction trans = new RewardTransaction();
		trans.setCreatedDate(new Date());
		trans.setDescription(desp);
		trans.setAmount(amount);
		trans.setPayeeName(payee);
		return trans;
	}
}

4.7 Lagre

AccountRepository strækker sig fra JpaRepository .

AccountRepository.java

package jcg.zheng.demo.spring.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import jcg.zheng.demo.spring.entity.Account;

@Repository
public interface AccountRepository extends JpaRepository<Account, Integer> {
	@Query("SELECT acct from Account acct WHERE acct.username = :username")
	Account findByUserName(@Param("username") String userName);
}

4.8 Spring Boot Web Application

Spring boot-webapplikation starter med tre trin. Først starter den public static void main metode. For det andet starter den Spring-konteksten, som inkluderer en initialisering af automatisk konfiguration, konfigurationer og anmærkninger. Til sidst starter den den automatisk konfigurerede indlejrede webserver (standard er Tomcat).

SpringBootWebApplication strækker sig fra SpringBootservletInitializer .

SpringBootWebApplication.java

package jcg.zheng.demo.spring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@EnableJpaRepositories(basePackages = "jcg.zheng.demo.spring")
public class SpringBootWebApplication extends SpringBootServletInitializer {

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(SpringBootWebApplication.class);
	}

	public static void main(String[] args) throws Exception {
		SpringApplication.run(SpringBootWebApplication.class, args);
	}

}

5. Web Application Unit Test

5.1 TestConfig

Opret en fjederkonfiguration til test.

TestConfig.java

package jcg.zheng.demo.spring;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = {"jcg.zheng.demo.spring"})
public class TestConfig {

}

5.2 HomeControllerTest

Test HomeController for udsigten til hjemmet.

HomeControllerTest.java

package jcg.zheng.demo.spring;

import static org.junit.Assert.assertEquals;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import jcg.zheng.demo.spring.controller.HomeController;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestConfig.class })
public class HomeControllerTest {
	
	@Autowired
	private HomeController welController;

	@Test
	public void welcome_view() {		
		Map model= new HashMap();
		Locale locale = new Locale.Builder().setLanguage("en").setRegion("MO").build();
		String viewName = welController.welcome(locale, model);
		assertEquals("home", viewName);
	}
	
}

5.3 UserControllerTest

Test UserController til den nye brugervisning.

UserControllerTest.java

package jcg.zheng.demo.spring;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.ui.Model;
import org.springframework.validation.support.BindingAwareModelMap;

import jcg.zheng.demo.spring.controller.UserController;
import jcg.zheng.demo.spring.model.User;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestConfig.class })
public class UserControllerTest {

	@Autowired
	private UserController controller;

	@Test
	public void newUser_view() {

		User user = new User();
		user.setUserName("not exist");
		Model model = new BindingAwareModelMap();
		String viewName = controller.user(user, model);
		assertEquals("newuser", viewName);
	}

}

6. Selentest

I dette trin vil jeg oprette et projekt – automation-demo som tester webapplikationen med Selenium i Google Chrome. Jeg vil demonstrere:

  • Sådan konfigurerer du Selenium WebDriver
  • Sådan finder du webelementet
  • Sådan interagerer du med webelementet
  • Sådan navigerer du på websiden
  • Sådan venter du på, at webelementet bliver tilgængeligt

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>mary.example.selenium</groupId>
	<artifactId>automation-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<dependencies>
		<dependency>
			<groupId>org.seleniumhq.selenium</groupId>
			<artifactId>selenium-java</artifactId>
			<version>2.41.0</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
</project>

6.1 Hjemmeside

Opret en HomePage til login-funktionen. Jeg vil bruge Selenium API til at finde indtastningsfeltet for brugernavn, indstille værdien og derefter klikke på login-knappen.

HomePage.java

package mary.demo.webtest;

import java.io.IOException;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class HomePage {
	private static final String LOGIN_ID = "login";
	private static final String USERNAME_ID = "username";

	private static final int TIMEOUT_SECONDS = 20;

	public String login(WebDriver driver, String un) throws IOException {

		WebDriverWait wait = new WebDriverWait(driver, TIMEOUT_SECONDS);
		WebElement userNameElement = wait.until(ExpectedConditions.elementToBeClickable(By.id(USERNAME_ID)));
		userNameElement.sendKeys(un);

		WebElement loginButtonElement = wait.until(ExpectedConditions.elementToBeClickable(By.id(LOGIN_ID)));
		loginButtonElement.click();

		String nextPageTitle = driver.getTitle();

		return nextPageTitle;

	}
}

6.2 NewUserPage

Opret en NewUserPage for at oprette en ny bruger. Jeg vil bruge Selenium API til at finde fornavnet ved Xpath værdi.

NewUserPage.java

package mary.demo.webtest;

import java.io.IOException;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class NewUserPage {

	private static final String FIRSTNAME_XPATH = "//input[@id='firstname']";

	private static final String SAVE_ID = "save";

	private static final String LASTTNAME_ID = "lasttname";

	private static final int TIMEOUT_SECONDS = 20;


	public String save(WebDriver driver, String firstName, String lastName) throws IOException {

		WebDriverWait wait = new WebDriverWait(driver, TIMEOUT_SECONDS);
		WebElement firstNameElement = wait.until(ExpectedConditions.elementToBeClickable(By.xpath(FIRSTNAME_XPATH)));
		firstNameElement.sendKeys(firstName);

		WebElement lastNameElement = wait.until(ExpectedConditions.elementToBeClickable(By.id(LASTTNAME_ID)));
		lastNameElement.sendKeys(lastName);

		WebElement saveButtonElement = wait.until(ExpectedConditions.elementToBeClickable(By.id(SAVE_ID)));
		saveButtonElement.click();

		String nextPageTitle = driver.getTitle();
	
		return nextPageTitle;

	}
}

6.3 ErrorPage

Opret en ErrorPage for brugeren, der har indtastet en forkert URL.

ErrorPage.java

package mary.demo.webtest;

import java.io.IOException;

import org.openqa.selenium.WebDriver;

public class ErrorPage {

	public String badUrl(WebDriver driver, String badUrl) throws IOException {

		driver.get(badUrl);
		String nextPageTitle = driver.getTitle();

		return nextPageTitle;

	}
}

7. Webbrowsertest

7.1 WebTestBase

Opret en WebTestBase for at konfigurere en Google Chrome Web-driver. Vi kan nemt understøtte andre browsere ved at skifte til en anden Seleniums WebDriver. Klik her for at downloade Selenium WebDriver.

WebTestBase.java

package mary.demo.webtest;

import java.io.IOException;

import org.junit.After;
import org.junit.Before;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class WebTestBase {
	protected WebDriver driver;
	protected ScreenshotHelper screenshotHelper;

	public WebTestBase() {
		super();
	}

	@Before
	public void setup() {
		System.setProperty("webdriver.chrome.driver", "C:\\MaryZheng\\tools\\webdriver\\chromedriver.exe");
		driver = new ChromeDriver();
		screenshotHelper = new ScreenshotHelper(driver);
	}

	@After
	public void close() throws IOException {
		driver.close();
	}

}

7.2 HomePageTest

Opret en HomePageTest for at teste login med ugyldig bruger i Google Chrome.

HomePageTest.java

package mary.demo.webtest;

import static org.junit.Assert.assertEquals;

import java.io.IOException;

import org.junit.Test;

public class HomePageTest extends WebTestBase{

	private HomePage homepage = new HomePage();;
	
	@Test
	public void login_invalid_user() throws IOException  {
		driver.get("http://localhost:8080/");
		screenshotHelper.saveScreenshot("home_screenshot.png");
		String nextPage = homepage.login(driver, "invalid");
		screenshotHelper.saveScreenshot("invalid_screenshot.png");
		assertEquals("New User", nextPage);
	}

}

7.3 ErrorPageTest

Opret en ErrorPageTest for at teste den forkerte url i Google Chrome.

ErrorPageTest.java

package mary.demo.webtest;

import static org.junit.Assert.assertEquals;

import java.io.IOException;

import org.junit.Test;

public class ErrorPageTest extends WebTestBase {

	private ErrorPage errorPage = new ErrorPage();;

	@Test
	public void login_invalid_user() throws IOException {
		String nextPage = errorPage.badUrl(driver, "http://localhost:8080/notexistUrl");
		screenshotHelper.saveScreenshot("error_screenshot.png");
		assertEquals("error", nextPage);
	}

}

7.4 NewUserPageTest

Opret en NewUserPageTest for at oprette en ny bruger i Google Chrome.

NewUserPageTest.java

package mary.demo.webtest;

import static org.junit.Assert.assertEquals;

import java.io.IOException;

import org.junit.Test;

public class NewUserPageTest extends WebTestBase {

	private HomePage homepage = new HomePage();
	private NewUserPage newUserPage = new NewUserPage();

	@Test
	public void create_new_user() throws IOException {
		driver.get("http://localhost:8080/");
		String nextPage = homepage.login(driver, "MaryZheng");
		if ("New User".equalsIgnoreCase(nextPage)) {
			nextPage = newUserPage.save(driver, "Alex", "Zheng");
			screenshotHelper.saveScreenshot("newuser_screenshot.png");
			assertEquals("User Account Detail", nextPage);
		}
		else {
			assertEquals("User Account Detail", nextPage);
		}
	}

}

7.5 ScreenshotHelper

Opret en ScreenshotHelper for at gemme en webside som et skærmbillede.

ScreenshotHelper.java

package mary.demo.webtest;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;

public class ScreenshotHelper {

	private WebDriver driver;

	public ScreenshotHelper(WebDriver driver) {
		super();
		this.driver = driver;
	}

	public void saveScreenshot(String screenshotFileName) throws IOException {
		File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
		FileUtils.copyFile(screenshot, new File(screenshotFileName));
	}

}

8. Demo

Start webapplikationen som en Java-applikation, og bekræft den med serverloggen.

Webapplikationsserverlog

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::       (v1.5.14.RELEASE)

2018-10-14 20:34:36.281  INFO 1384 --- [           main] j.z.d.spring.SpringBootWebApplication    : Starting SpringBootWebApplication on SL2LS431841 with PID 1384 (C:\gitworkspace\spring-boot-web-jsp-demo\target\classes started by Shu.Shan in C:\gitworkspace\spring-boot-web-jsp-demo)
2018-10-14 20:34:36.284  INFO 1384 --- [           main] j.z.d.spring.SpringBootWebApplication    : No active profile set, falling back to default profiles: default
2018-10-14 20:34:36.387  INFO 1384 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1c72da34: startup date [Sun Oct 14 20:34:36 CDT 2018]; root of context hierarchy
2018-10-14 20:34:37.950  INFO 1384 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$EnhancerBySpringCGLIB$2ec905e1] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-10-14 20:34:38.863  INFO 1384 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2018-10-14 20:34:38.899  INFO 1384 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2018-10-14 20:34:38.900  INFO 1384 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.31
2018-10-14 20:34:39.303  INFO 1384 --- [ost-startStop-1] org.apache.jasper.servlet.TldScanner     : At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
2018-10-14 20:34:39.308  INFO 1384 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2018-10-14 20:34:39.309  INFO 1384 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2927 ms
2018-10-14 20:34:39.517  INFO 1384 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2018-10-14 20:34:39.522  INFO 1384 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-10-14 20:34:39.522  INFO 1384 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-10-14 20:34:39.522  INFO 1384 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-10-14 20:34:39.522  INFO 1384 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2018-10-14 20:34:40.091  INFO 1384 --- [           main] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'default'
2018-10-14 20:34:40.116  INFO 1384 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [
	name: default
	...]
2018-10-14 20:34:40.223  INFO 1384 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate Core {5.0.12.Final}
2018-10-14 20:34:40.225  INFO 1384 --- [           main] org.hibernate.cfg.Environment            : HHH000206: hibernate.properties not found
2018-10-14 20:34:40.227  INFO 1384 --- [           main] org.hibernate.cfg.Environment            : HHH000021: Bytecode provider name : javassist
2018-10-14 20:34:40.285  INFO 1384 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
2018-10-14 20:34:40.546  INFO 1384 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2018-10-14 20:34:41.120  INFO 1384 --- [           main] org.hibernate.tool.hbm2ddl.SchemaExport  : HHH000227: Running hbm2ddl schema export
2018-10-14 20:34:41.153  INFO 1384 --- [           main] org.hibernate.tool.hbm2ddl.SchemaExport  : HHH000230: Schema export complete
2018-10-14 20:34:41.225  INFO 1384 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2018-10-14 20:34:41.517  INFO 1384 --- [           main] o.h.h.i.QueryTranslatorFactoryInitiator  : HHH000397: Using ASTQueryTranslatorFactory
2018-10-14 20:34:42.079  INFO 1384 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1c72da34: startup date [Sun Oct 14 20:34:36 CDT 2018]; root of context hierarchy
2018-10-14 20:34:42.301  INFO 1384 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String jcg.zheng.demo.spring.controller.HomeController.welcome(java.util.Locale,java.util.Map)
2018-10-14 20:34:42.303  INFO 1384 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/user],methods=[POST]}" onto public java.lang.String jcg.zheng.demo.spring.controller.UserController.user(jcg.zheng.demo.spring.model.User,org.springframework.ui.Model)
2018-10-14 20:34:42.303  INFO 1384 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/user/save],methods=[POST]}" onto public java.lang.String jcg.zheng.demo.spring.controller.UserController.saveuser(jcg.zheng.demo.spring.entity.Account,org.springframework.ui.Model)
2018-10-14 20:34:42.306  INFO 1384 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-10-14 20:34:42.307  INFO 1384 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-10-14 20:34:42.350  INFO 1384 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-10-14 20:34:42.350  INFO 1384 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-10-14 20:34:42.403  INFO 1384 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-10-14 20:34:42.709  INFO 1384 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-10-14 20:34:42.769  INFO 1384 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2018-10-14 20:34:42.775  INFO 1384 --- [           main] j.z.d.spring.SpringBootWebApplication    : Started SpringBootWebApplication in 6.879 seconds (JVM running for 7.33)

Kør mvn clean install for automation-demo projekt. Se den starte Google Chrome og udføre alle testsager.

Resultater af automatiseringstest

C:\gitworkspace\automation-demo>mvn clean  install
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=512m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=512m; support was removed in 8.0
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------< mary.example.selenium:automation-demo >----------------
[INFO] Building automation-demo 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ automation-demo ---
[INFO] Deleting C:\gitworkspace\automation-demo\target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ automation
-demo ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources,i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ automation-demo
 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 3 source files to C:\gitworkspace\automation-demo\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ automation-demo ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources,i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ automation-demo ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 5 source files to C:\gitworkspace\automation-demo\target\test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ automation-demo --
-
[INFO] Surefire report directory: C:\gitworkspace\automation-demo\target\surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running mary.demo.webtest.ErrorPageTest
Starting ChromeDriver 2.41.578737 (49da6702b16031c40d63e5618de03a32ff6c197e) on 
port 18877
Only local connections are allowed.

DevTools listening on ws://127.0.0.1:55807/devtools/browser/d86b6c77-d33c-4546-a93f-a7232af276f9
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 10.328 sec
Running mary.demo.webtest.HomePageTest
Starting ChromeDriver 2.41.578737 (49da6702b16031c40d63e5618de03a32ff6c197e) on 
port 36077
Only local connections are allowed.

DevTools listening on ws://127.0.0.1:55836/devtools/browser/6dc4f429-e1d5-45ce-ac34-5c699975ec36
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 9.221 sec
Running mary.demo.webtest.NewUserPageTest
Starting ChromeDriver 2.41.578737 (49da6702b16031c40d63e5618de03a32ff6c197e) on
port 39499
Only local connections are allowed.

DevTools listening on ws://127.0.0.1:55862/devtools/browser/74aad938-bd3d-4068-9508-d51c066345ff
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 9.052 sec

Results :

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ automation-demo ---
[INFO] Building jar: C:\gitworkspace\automation-demo\target\automation-demo-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ automation-demo
---
[INFO] Installing C:\gitworkspace\automation-demo\target\automation-demo-0.0.1-SNAPSHOT.jar to C:\repo\mary\example\selenium\automation-demo\0.0.1-SNAPSHOT\auto
mation-demo-0.0.1-SNAPSHOT.jar
[INFO] Installing C:\gitworkspace\automation-demo\pom.xml to C:\repo\mary\example\selenium\automation-demo\0.0.1-SNAPSHOT\automation-demo-0.0.1-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 35.629 s
[INFO] Finished at: 2018-10-14T20:36:52-05:00
[INFO] ------------------------------------------------------------------------

C:\gitworkspace\automation-demo>

Bekræft det baseret på de gemte skærmbilleder.

Figur 3 Hjemmeside

Figur 4 Ugyldig bruger

Figur 5 Brugerdetaljer

9. Selen-webapplikationstest – resumé

I denne artikel byggede jeg en Spring Boot-webapplikation og lavede derefter automatiseringstestcases med Selenium i Google Chrome.

10. Download kildekoden

Denne tutorial består af to Maven-projekter:

  • spring-boot-web-jsp-demo – opret en webapplikation med Spring MVC framework.
  • automation-demo – test ovenstående webapplikation med Selenium.
selen fjederstøvle Spring MVC
Java tag