目录
一、设计测试用例
二、自动化测试
1、导入依赖
1、登录页面
3、列表页面
4、详情页面
5、写博客页面
6、完善
三、总结
一、设计测试用例
二、自动化测试
使用selenium4 + Junit5单元测试框架,来进行简单的自动化测试。
1、导入依赖
创建Maven项目,并在pom.xml中导入如下依赖:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-commons</artifactId>
<version>1.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-reporting</artifactId>
<version>1.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<version>1.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
公共类:
public class CommonDriver {
//使用单例模式创建驱动
private static ChromeOptions options = new ChromeOptions();
public static ChromeDriver driver = null;
public static ChromeDriver getDriver(){
if (driver == null) {
options.addArguments("--remote-allow-origins=*");
driver = new ChromeDriver(options);
//创建驱动的时候就加上隐式等待
//整个测试就都会隐式等待了
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
}
return driver;
}
//获取屏幕截图
//同时通过参数str来判断是哪个测试类中的截图
public static void getScreenshot(String str) throws IOException {
List<String> times = getTime();
//生成的文件夹的格式:./src/test/autotest-2023-05-21/ListTest-20230521-205350-毫秒.png
//再加上文件的名称filename
String filename = "./src/test/autotest-"+times.get(0) +"/"+ str +"-" + times.get(1) + ".png";
File srcfile = driver.getScreenshotAs(OutputType.FILE);
//把屏幕截图放到指定的路径下
FileUtils.copyFile(srcfile,new File(filename));
}
//获取文件名和文件夹名,通过时间区分屏幕截图
public static List<String> getTime(){
//文件格式 20230521-205350-毫秒
SimpleDateFormat sim1 = new SimpleDateFormat("yyyyMMdd-HHmmssSS");
//文件夹格式 2023-05-21
SimpleDateFormat sim2 = new SimpleDateFormat("yyyy-MM-dd");
String filename = sim1.format(System.currentTimeMillis());
String dirname = sim2.format(System.currentTimeMillis());
List<String> list = new ArrayList<>();
list.add(dirname);
list.add(filename);
return list;
}
public static void close() {
driver.quit();
}
}
1、登录页面
主要测试了:
代码:
① 动态传参,在ParamsUtil 类中写了数据的来源,在这里直接调用即可。但用户名和密码有对应关系,所以使用了Map和Set。同时还要注意,传的是Stream流。
② 多参数,在正确的用户名和错误的密码测试中,使用多参数。直接使用注解CsvSource,并传入数据。这里传入的数据都会被测试到。
public class LoginTest {
static ChromeDriver driver = CommonDriver.getDriver();
@BeforeEach
public void getUrl(){
driver.get("http://119.91.45.80:8080/my_blog_system/blog_login.html");
}
/*
* 输入正确的用户名和正确的密码能否登录
* 使用动态参数
* */
@ParameterizedTest
//需要一个同名的静态方法
@MethodSource
public void LoginTrue(String name,String password) {
driver.findElement(By.cssSelector("#username")).sendKeys(name);
driver.findElement(By.cssSelector("#password")).sendKeys(password);
driver.findElement(By.cssSelector("#submit")).click();
String str2 = driver.findElement(By.cssSelector("body > div.container > div.left > div > div:nth-child(4) > span:nth-child(1)")).getText();
Assertions.assertEquals("文章",str2);
}
//给LoginTreu提供数据来源
static Stream<Arguments> LoginTrue(){
//在公共类中获取数据
ParamsUtil paramsUtil = new ParamsUtil();
String username = paramsUtil.getuserName();
String password = paramsUtil.getTruetextMap().get(username);
return Stream.of(Arguments.arguments(username,password));
}
/*
* 输入正确的用户名和错误的密码能否登录
* 多参数
* */
@ParameterizedTest
@CsvSource(value = {"www,qwe","王文哲,sda","张三,dsv4"})
public void LoginFalse(String username,String password){
driver.findElement(By.cssSelector("#username")).sendKeys(username);
driver.findElement(By.cssSelector("#password")).sendKeys(password);
driver.findElement(By.cssSelector("#submit")).click();
String str = driver.findElement(By.cssSelector("body")).getText();
Assertions.assertEquals("当前用户登录用户名或密码错误",str);
}
/*
* 输入空的用户名和空的密码
* */
@Test
public void LoginNull(){
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#password")).clear();
driver.findElement(By.cssSelector("#submit")).click();
String str = driver.findElement(By.cssSelector("body")).getText();
Assertions.assertEquals("当前用户登录用户名或密码为空",str);
// driver.navigate().back();
}
/*
* 登录文字是否正确
* */
@Test
public void testDenglu(){
String str = driver.findElement(By.cssSelector("body > div.login-container > form > div > h3")).getText();
Assertions.assertEquals("登录",str);
}
/*
*用户名输入框是否正确
* */
@Test
public void testNameInput(){
WebElement element = driver.findElement(By.cssSelector("#username"));
Assertions.assertNotNull(element);
}
/*
* 密码输入框是否正确
* */
@Test
public void testPasswordInput(){
WebElement element = driver.findElement(By.cssSelector("#password"));
Assertions.assertNotNull(element);
}
/*
* 提交按钮是否正确
* */
@Test
public void testSubmit(){
String str = driver.findElement(By.xpath("//*[@id=\"submit\"]")).getAttribute("value");
Assertions.assertEquals("提交",str);
}
}
PrarmsUtil类(数据来源):
public class ParamsUtil {
private Map<String,String> truetextMap = new HashMap<>();
public Map<String,String> getTruetextMap() {
truetextMap.put("www","1234");
truetextMap.put("王文哲","wwz");
truetextMap.put("张三","123");
return truetextMap;
}
public String getuserName(){
//这里需要随机返回一组数据
truetextMap = getTruetextMap();
Set<String> set = truetextMap.keySet();
Object[] arr1 = set.toArray();
String[] arr2 = new String[set.size()];
for (int i = 0; i < arr2.length;i++) {
arr2[i] = (String)arr1[i];
}
//Math.random返回的是0~1的小数,因此要乘以数组的长度。
int index =(int)(Math.random()*arr2.length);
return arr2[index];
}
}
3、列表页面
代码:
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class ListTest {
static ChromeDriver driver = CommonDriver.getDriver();
/*
* 访问博客列表页面
* */
@BeforeEach
public void getURL(){
driver.get("http://119.91.45.80:8080/my_blog_system/blog_list.html");
}
/*
* 当用户未登录的时候,能否正确提示用户,并返回到登录页面让用户进行登录
* */
@Test
@Order(1)
public void testLogin(){
Alert alert = driver.switchTo().alert();
alert.accept();
//再检查是否跳转到登录页面
String str = driver.findElement(By.cssSelector("body > div.login-container > form > div > h3")).getText();
Assertions.assertEquals("登录",str);
//如果是登录页面
//进行登录
driver.findElement(By.cssSelector("#username")).sendKeys("王文哲");
driver.findElement(By.cssSelector("#password")).sendKeys("wwz");
driver.findElement(By.cssSelector("#submit")).click();
String str2 = driver.findElement(By.cssSelector("body > div.container > div.left > div > div:nth-child(4) > span:nth-child(1)")).getText();
Assertions.assertEquals("文章",str2);
}
/*
* 点击写博客,正确跳转到写博客页面
* */
@Test
@Order(2)
public void testEdit(){
driver.findElement(By.cssSelector("body > div.nav > a:nth-child(5)")).click();
String str = driver.findElement(By.cssSelector("#title")).getAttribute("placeholder");
Assertions.assertEquals("在此处输入标题",str);
String str2 = driver.findElement(By.cssSelector("#submit")).getAttribute("value");
Assertions.assertEquals("发布文章",str2);
}
/*
* 点击主页,还是在列表页面
* */
@Test
public void testList(){
driver.findElement(By.cssSelector("body > div.nav > a:nth-child(4)")).click();
String text1 = driver.findElement(By.cssSelector("body > div.container > div.left > div > div:nth-child(4) > span:nth-child(1)")).getText();
String text2 = driver.findElement(By.cssSelector("body > div.container > div.left > div > div:nth-child(4) > span:nth-child(2)")).getText();
Assertions.assertEquals("文章",text1);
Assertions.assertEquals("分类",text2);
}
/*
* 点击注销,跳转到登录页面
* */
@Test
public void testDelete(){
driver.findElement(By.xpath("/html/body/div[1]/a[3]")).click();
String text1 = driver.findElement(By.xpath("/html/body/div[2]/form/div/h3")).getText();
String text2 = driver.findElement(By.cssSelector("#submit")).getAttribute("value");
Assertions.assertEquals("登录",text1);
Assertions.assertEquals("提交",text2);
}
/*
* 点击地址,正确跳转到我的gitee页面
* */
@Test
public void testGitee(){
driver.findElement(By.cssSelector("body > div.container > div.left > div > a")).click();
String text1 = driver.findElement(By.cssSelector("#git-header-nav > div > div > div.item.gitosc-logo > a > img.ui.inline.black.image")).getAttribute("alt");
Assertions.assertEquals("Gitee — 基于 Git 的代码托管和研发协作平台",text1);
}
/*
* 点击列表页的查看全文按钮,跳转到详情页面
* */
@Test
public void testView() {
driver.findElement(By.cssSelector("body > div.container > div.right > div:nth-child(1) > a")).click();
String text = driver.findElement(By.cssSelector("body > div.container > div.left > div > div:nth-child(4) > span:nth-child(1)")).getText();
Assertions.assertEquals("文章",text);
}
/*
* 文章文字是否正确
* */
@Test
public void testWz(){
String text = driver.findElement(By.cssSelector("body > div.container > div.left > div > div:nth-child(4) > span:nth-child(1)")).getText();
Assertions.assertEquals("文章",text);
}
/*
* 分类文字是否正确
* */
@Test
public void testFl(){
String text = driver.findElement(By.cssSelector("body > div.container > div.left > div > div:nth-child(4) > span:nth-child(2)")).getText();
Assertions.assertEquals("分类",text);
}
@AfterAll
public static void close(){
driver.close();
}
}
4、详情页面
代码:
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class DetailTest {
static ChromeDriver driver = CommonDriver.getDriver();
@BeforeEach
public void getUrl(){
//因为详情页只能从列表页,点击查看全文进入
driver.get("http://119.91.45.80:8080/my_blog_system/blog_list.html");
}
/*
* 当用户未登录时,弹窗提示,并跳转到登录页面
* */
@Test
@Order(1)
public void testLogin(){
Alert alert = driver.switchTo().alert();
alert.accept();
//跳转到登录页面
String str = driver.findElement(By.cssSelector("body > div.login-container > form > div > h3")).getText();
Assertions.assertEquals("登录",str);
//登录
driver.findElement(By.cssSelector("#username")).sendKeys("王文哲");
driver.findElement(By.cssSelector("#password")).sendKeys("wwz");
driver.findElement(By.cssSelector("#submit")).click();
//再到博客详情页
driver.findElement(By.cssSelector("body > div.container > div.right > div:nth-child(1) > a")).click();
}
/*
* 文章文字是否正确
* */
@Test
public void testWz(){
String text = driver.findElement(By.cssSelector("body > div.container > div.left > div > div:nth-child(4) > span:nth-child(1)")).getText();
Assertions.assertEquals("文章",text);
}
/*
* 分类文字是否正确
* */
@Test
public void testFl(){
String text = driver.findElement(By.cssSelector("body > div.container > div.left > div > div:nth-child(4) > span:nth-child(2)")).getText();
Assertions.assertEquals("分类",text);
}
@AfterAll
public static void close(){
driver.close();
}
}
5、写博客页面
代码:
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class EditTest {
ChromeDriver driver = CommonDriver.getDriver();
@BeforeEach
public void getUrl(){
driver.get("http://119.91.45.80:8080/my_blog_system/blog_edit.html");
}
/*
* 当用户未登录时,弹窗提示,并跳转到登录页面
* */
@Test
@Order(1)
public void testLogin(){
driver.findElement(By.cssSelector("body > div.nav > a:nth-child(4)")).click();
Alert alert = driver.switchTo().alert();
alert.accept();
//跳转到登录页面
String str = driver.findElement(By.cssSelector("body > div.login-container > form > div > h3")).getText();
Assertions.assertEquals("登录",str);
//登录
driver.findElement(By.cssSelector("#username")).sendKeys("王文哲");
driver.findElement(By.cssSelector("#password")).sendKeys("wwz");
driver.findElement(By.cssSelector("#submit")).click();
}
/*
* 编写博客,点击发布按钮,跳转到列表页面
* */
@Test
public void testRelease(){
driver.findElement(By.cssSelector("#title")).sendKeys("日记2");
driver.findElement(By.cssSelector("#editor > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll")).sendKeys("今天是521,真开心");
driver.findElement(By.cssSelector("#submit")).click();
//看是否跳转到列表页面
String text1 = driver.findElement(By.cssSelector("body > div.container > div.left > div > div:nth-child(4) > span:nth-child(1)")).getText();
String text2 = driver.findElement(By.cssSelector("body > div.container > div.left > div > div:nth-child(4) > span:nth-child(2)")).getText();
Assertions.assertEquals("文章",text1);
Assertions.assertEquals("分类",text2);
}
/*
* 点击主页,跳转到列表页面
* */
@Test
public void testList(){
driver.findElement(By.cssSelector("body > div.nav > a:nth-child(4)")).click();
//看是否跳转到列表页面
String text1 = driver.findElement(By.cssSelector("body > div.container > div.left > div > div:nth-child(4) > span:nth-child(1)")).getText();
String text2 = driver.findElement(By.cssSelector("body > div.container > div.left > div > div:nth-child(4) > span:nth-child(2)")).getText();
Assertions.assertEquals("文章",text1);
Assertions.assertEquals("分类",text2);
}
/*
* 点击注销,跳转到登录页面
* */
@Test
public void testDelete(){
driver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)")).click();
//跳转到登录页面
String str = driver.findElement(By.cssSelector("body > div.login-container > form > div > h3")).getText();
Assertions.assertEquals("登录",str);
//登录
driver.findElement(By.cssSelector("#username")).sendKeys("王文哲");
driver.findElement(By.cssSelector("#password")).sendKeys("wwz");
driver.findElement(By.cssSelector("#submit")).click();
}
/*
* 点击写博客,还是在写博客页面
* */
@Test
public void testEdit(){
driver.findElement(By.cssSelector("body > div.nav > a:nth-child(5)")).click();
String str = driver.findElement(By.cssSelector("#title")).getAttribute("placeholder");
Assertions.assertEquals("在此处输入标题",str);
String str2 = driver.findElement(By.cssSelector("#submit")).getAttribute("value");
Assertions.assertEquals("发布文章",str2);
}
/*
* 标题栏输入框是否正确显示
* */
@Test
public void testInput(){
String str = driver.findElement(By.cssSelector("#title")).getAttribute("placeholder");
Assertions.assertEquals("在此处输入标题",str);
}
/*
* 发布按钮是否能正确显示
* */
@Test
public void testSubmit(){
String str2 = driver.findElement(By.cssSelector("#submit")).getAttribute("value");
Assertions.assertEquals("发布文章",str2);
}
}
6、完善
(1)使用套件
(2)使用屏幕截图
(3)将close方法都提出来,在DriverQuitTest中写close方法,在套件中使用。
套件:
@Suite
@SelectClasses({LoginTest.class,ListTest.class,EditTest.class,DetailTest.class,DriverQuitTest.class})
public class RunSuit {
}
屏幕截图:
//获取屏幕截图
//同时通过参数str来判断是哪个测试类中的截图
public static void getScreenshot(String str) throws IOException {
List<String> times = getTime();
//生成的文件夹的格式:./src/test/autotest-2023-05-21/ListTest-20230521-205350-毫秒.png
//再加上文件的名称filename
String filename = "./src/test/autotest-"+times.get(0) +"/"+ str +"-" + times.get(1) + ".png";
File srcfile = driver.getScreenshotAs(OutputType.FILE);
//把屏幕截图放到指定的路径下
FileUtils.copyFile(srcfile,new File(filename));
}
//获取文件名和文件夹名,通过时间区分屏幕截图
public static List<String> getTime(){
//文件格式 20230521-205350-毫秒
SimpleDateFormat sim1 = new SimpleDateFormat("yyyyMMdd-HHmmssSS");
//文件夹格式 2023-05-21
SimpleDateFormat sim2 = new SimpleDateFormat("yyyy-MM-dd");
String filename = sim1.format(System.currentTimeMillis());
String dirname = sim2.format(System.currentTimeMillis());
List<String> list = new ArrayList<>();
list.add(dirname);
list.add(filename);
return list;
}
DriverQuitTest类:
public class DriverQuitTest extends CommonDriver{
private static ChromeDriver driver = getDriver();
@Test
void driverQuit(){
driver.quit();
}
}
三、总结
(1)使用了注解,避免生成过多的对象,造成资源和时间的浪费。
(2)通过static修饰静态变量,全局只创建了一次驱动对象,避免重复创造驱动对象造成时间的浪费。
(3)使用参数化,保持用例的简介,提高了代码的可读性。
(4)使用测试套件,一次执行所有我们想要运行的自动化用例。
(5)使用等待(隐式等待+强制等待),提高自动化指令的稳定性,降低自动化出现误报的概率。
(6)使用屏幕截图,方便问题的追溯和解决。