软件测试自动化Java篇【Selenium+Junit 5】

news2024/12/24 9:05:38

文章目录

  • Selenium
    • 环境部署
    • 自动化测试例子
    • 常见的元素操作
    • 窗口
    • 等待
    • 浏览器的操作
    • 弹窗
    • 选择器
    • 执行脚本
    • 文件上传
    • 浏览器参数
  • Junit 5
    • 导入依赖
    • Junit 4 和 Junit5 注解对比
    • 断言
    • 测试顺序
    • 参数化
      • 单参数
      • 多参数
      • 动态参数
    • 测试套件
      • 指定类来运行测试用例
      • 指定包名来运行包下测试用例

Selenium

为什么选择selenium作为我们的web自动化测试工具?

  1. 开源免费
  2. 支持多浏览器
  3. 支持多系统
  4. 支持多语言【Java,Python,C#,Rubby,JavaScript,Kolin】
  5. selenium包提供了很多可供测试使用的API

环境部署

Chrome浏览器
在这里插入图片描述
Chrome驱动【驱动器版本要和浏览器版本对应越详细越好】
在这里插入图片描述
然后把驱动包放在安装jdk的bin目录下
在这里插入图片描述

selenium工具包
在这里插入图片描述

自动化测试例子

分为接口自动化测试和UI自动化测试

UI自动化测试包含界面测试

我们都知道用户的设备很多,有手机,平板和电脑。这些设备运行的项目在发布之初都需要经过测试,这些测试又分为web自动化测试 和 移动端自动化测试。这里以 web自动化测试 为主。
自动化测试的工具有很多,比如 QTP、Selenium,Jmeter、Loadrunner。本期就以 Selenium 为主进行介绍最简单的自动化测试

测试点击百度

import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;

private class AutoTest {
    // 模拟百度搜索关键词
    public static void baiduSearchKey() {
        String url = "https://www.baidu.com";
        String keyWords = "新闻";
        ChromeDriver chromeDriver=new ChromeDriver();
        try {
            Thread.sleep(5000);
            // 输入框输入文字
            chromeDriver.findElement(new By.ByXPath("//*[@id=\"kw\"]")).sendKeys(keyWords);
            Thread.sleep(5000);
            // 点击搜索按钮
            chromeDriver.findElement(new By.ByXPath("//*[@id=\"su\"]")).click();
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        chromeDriver.quit();
    }
    
    public static void main(String[] args) {
    		baiduSearchKey();
    }
}

获取元素方法有很多,但经常用的是 ByXPath和ByCssSelector
在这里插入图片描述
ByXpath
/:相当于子级一层一层的完整xpath路径
//:相当于越级,直接越过之前的html标签,根据id来查询标签

后文中只有之前的几个操作时获取部分xpath,以后的获取都是完整的获取xpath路径【看//数量即可知道是否根据带通配符或者id查询来区别】

常见的元素操作

  1. 输入文本 sendKeys(str)
    对元素操作的前提是元素能够被找到,如果查询的元素不是可编辑的文本标签,那么前端不受影响,后端也不报错。按照程序的设定正常退出
  2. 点击操作 click()&提交操作 submit()
private static void TestBlogLogin() {
    // 输入邮箱
    String url = "http://localhost:8080/index.html";
    String email = "1969612859@qq.com";
    String password = "admin";
    ChromeDriver chromeDriver = new ChromeDriver();
    chromeDriver.get(url);
    try {
        Thread.sleep(3000);
        // 输入邮箱
        chromeDriver.findElement(new By.ByXPath("//*[@id=\"login\"]/div[1]/input")).sendKeys(email);
        Thread.sleep(3000);
        // 输入密码
        chromeDriver.findElement(new By.ByXPath("//*[@id=\"login\"]/div[2]/input")).sendKeys(password);
        Thread.sleep(3000);
        // 点击登录【也可以回车登录】
        chromeDriver.findElement(new By.ByXPath("/html/body/div/div/div/form[1]/button")).click();
        //chromeDriver.findElement(new By.ByXPath("/html/body/div/div/div/form[1]/button")).submit();
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    chromeDriver.quit();
}

提交操作 submit 仅适用于form表单之类的submit操作,一般用的少
Selenium更推荐使用 click ,功能比 submit 更丰富

  1. 清除操作 clear()
private static void TestBlogLogin() {
    // 输入正确的邮箱和密码
    String url = "http://localhost:8080/index.html";
    String email = "1969612859@qq.com";
    String password = "admin";
    ChromeDriver chromeDriver = new ChromeDriver();
    chromeDriver.get(url);
    try {
        Thread.sleep(3000);
        chromeDriver.findElement(new By.ByXPath("//*[@id=\"login\"]/div[1]/input")).sendKeys("手滑输入错了");
        Thread.sleep(3000);
        // 清除文本
        chromeDriver.findElement(new By.ByXPath("//*[@id=\"login\"]/div[1]/input")).clear();
        Thread.sleep(3000);
        chromeDriver.findElement(new By.ByXPath("//*[@id=\"login\"]/div[1]/input")).sendKeys(email);
        Thread.sleep(3000);
        chromeDriver.findElement(new By.ByXPath("//*[@id=\"login\"]/div[2]/input")).sendKeys(password);
        Thread.sleep(3000);
        chromeDriver.findElement(new By.ByXPath("/html/body/div/div/div/form[1]/button")).click();
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    chromeDriver.quit();
}
  1. 获取文本值 getText()
private static void TestBlogLogin() {
    // 输入正确的邮箱和密码
    String url = "http://localhost:8080/index.html";
    String email = "1969612859@qq.com";
    String password = "admin";
    ChromeDriver chromeDriver = new ChromeDriver();
    chromeDriver.get(url);
    try {
        // 获取第一道题目的标题
        String text = chromeDriver.findElement(new By.ByXPath("/html/body/div/div/div[2]/div/div/div/div/div/div/table/tbody/tr[1]/td[2]")).getText();
        System.out.println("getText()获取到的文本:" + text);
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    chromeDriver.quit();
}

在这里插入图片描述

在这里插入图片描述

发现即使 URL 跳转到了别的网页,由于线程休眠3s的缘故,并不会很快就去获取页面元素而是给了页面一个加载的时间,因此可以完全获取到跳转到其它页面的标签

  1. 获取属性 getAttribute(attr)
    在这里插入图片描述
private static void TestBlogLogin() {
    // 输入正确的邮箱和密码
    String url = "http://localhost:8080/index.html";
    String email = "1969612859@qq.com";
    String password = "admin";
    ChromeDriver chromeDriver = new ChromeDriver();
    chromeDriver.get(url);
    try {
        // 获取属性
        String role = chromeDriver.findElement(new By.ByXPath("/html/body/div/div/div[2]/div/div/div/div/div/div/table/tbody/tr[1]/td[5]/div/div")).getAttribute("role");
        Thread.sleep(3000);
        String aria_valuenow = chromeDriver.findElement(new By.ByXPath("/html/body/div/div/div[2]/div/div/div/div/div/div/table/tbody/tr[1]/td[5]/div/div")).getAttribute("aria-valuenow");
        Thread.sleep(3000);
        String aria_valuemin = chromeDriver.findElement(new By.ByXPath("/html/body/div/div/div[2]/div/div/div/div/div/div/table/tbody/tr[1]/td[5]/div/div")).getAttribute("aria-valuemin");
        Thread.sleep(3000);
        String aClass = chromeDriver.findElement(new By.ByXPath("/html/body/div/div/div[2]/div/div/div/div/div/div/table/tbody/tr[1]/td[5]/div/div")).getAttribute("class");
        Thread.sleep(3000);
        String style = chromeDriver.findElement(new By.ByXPath("/html/body/div/div/div[2]/div/div/div/div/div/div/table/tbody/tr[1]/td[5]/div/div")).getAttribute("style");
        Thread.sleep(3000);
        System.out.printf("role=%s\naria_valuenow=%s\naria_valuemin=%s\naClass=%s\nstyle=%s\n", role, aria_valuenow, aria_valuemin, aClass, style);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    chromeDriver.quit();
}

在这里插入图片描述
6. 获取标题getTitle()和urlgetCurrenturl()

// 模拟百度搜索关键词
private static void baiduSearchKey() {
    String url = "https://www.baidu.com";
    String keyWords = "新闻";
    ChromeDriver chromeDriver = new ChromeDriver();
    chromeDriver.get(url);
    HashMap<String, List<String>> info = new HashMap<>();
    try {
        Thread.sleep(5000);
        ArrayList<String> before = new ArrayList<String>() {{
            add(chromeDriver.getTitle());
            add(chromeDriver.getCurrentUrl());
        }};
        // 输入框输入文字
        chromeDriver.findElement(new By.ByXPath("//*[@id=\"kw\"]")).sendKeys(keyWords);
        Thread.sleep(5000);
        // 点击搜索按钮
        chromeDriver.findElement(new By.ByXPath("//*[@id=\"su\"]")).click();
        Thread.sleep(5000);
        System.out.println("搜索之后");
        ArrayList<String> after = new ArrayList<String>() {{
            add(chromeDriver.getTitle());
            add(chromeDriver.getCurrentUrl());
        }};
        info.put("before", before);
        info.put("after", after);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    chromeDriver.quit();
    System.out.println(info);
}

在这里插入图片描述

窗口

  1. 窗口大小
    窗口大小的设置:最大/小化,全屏窗口,手动设置窗口大小
private static void windControl() {
    String url = "https://www.baidu.com";
    ChromeDriver chromeDriver = new ChromeDriver();
    chromeDriver.get(url);
    try {
        Thread.sleep(1500);
        // 窗口最大化
        chromeDriver.manage().window().maximize();
        Thread.sleep(1500);
        // 窗口最小化
        chromeDriver.manage().window().minimize();
        Thread.sleep(1500);
        // 全屏
        chromeDriver.manage().window().fullscreen();
        Thread.sleep(1500);
        // 手动设置窗口大小
        chromeDriver.manage().window().setSize(new Dimension(1024, 768));
        Thread.sleep(1500);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    chromeDriver.quit();
}
  1. 切换窗口
    一般的窗口切换可能会导致找不到元素的BUG
private static void windControl() {
    String url = "https://www.baidu.com";
    ChromeDriver chromeDriver = new ChromeDriver();
    chromeDriver.get(url);
    try {
        Thread.sleep(1500);
        // 点击准备跳转
        chromeDriver.findElement(new By.ByXPath("/html/body/div[1]/div[1]/div[3]/a[6]")).click();
        String value = chromeDriver.findElement(new By.ByXPath("/html/body/div[1]/div[1]/div[3]/div/div/div/div/div/div[2]/form/span[2]/input")).getAttribute("value");
        System.out.println(value);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    chromeDriver.quit();
}

在这里插入图片描述
由于无法获取跳转页面的元素,所以就会报错。因此需要一个页面切换功能

private static void windControl() {
    String url = "https://www.baidu.com";
    ChromeDriver chromeDriver = new ChromeDriver();
    chromeDriver.get(url);
    try {
        Thread.sleep(1500);
        // 获取当前页面句柄
        String currentHandle = chromeDriver.getWindowHandles().toString();
        System.out.println("当前首页句柄:"+currentHandle);
        Thread.sleep(1500);
        // 点击准备跳转
        chromeDriver.findElement(new By.ByXPath("/html/body/div[1]/div[1]/div[3]/a[6]")).click();
        // 获取跳转之后所有标签页的句柄
        Set<String> handles = chromeDriver.getWindowHandles();
        for (String handle : handles) {
            if (currentHandle != handle){
                chromeDriver.switchTo().window(handle);
            }
        }
        String value = chromeDriver.findElement(new By.ByXPath("/html/body/div[1]/div[1]/div[3]/div/div/div/div/div/div[2]/form/span[2]/input")).getAttribute("value");
        System.out.println(value);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    chromeDriver.quit();
}

运行的退出码已经恢复正常
在这里插入图片描述
3. 屏幕截图
需要导入一个常用的commons-io库

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>
private static void windControl() {
    String url = "https://www.baidu.com";
    ChromeDriver chromeDriver = new ChromeDriver();
    chromeDriver.get(url);
    try {
        Thread.sleep(1500);
        // 点击准备跳转
        chromeDriver.findElement(new By.ByXPath("/html/body/div[1]/div[1]/div[3]/a[6]")).click();
        File screenshotAs = chromeDriver.getScreenshotAs(OutputType.FILE);
        // 把屏幕截图好的文件放在指定的路径下
        String fileName = "my.png";
        try {
            FileUtils.copyFile(screenshotAs, new File(fileName));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String value = chromeDriver.findElement(new By.ByXPath("/html/body/div[1]/div[1]/div[3]/div/div/div/div/div/div[2]/form/span[2]/input")).getAttribute("value");
        System.out.println(value);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    chromeDriver.quit();
}

这里会发生报错,由于屏幕截图原因。所以会保存当时的获取状态。
在这里插入图片描述
会把当前的现场截图保存下来【由于没有切换页面,所以也就会报错,截图代码发生在报错之前的代码,所以也就保留了事故现场

等待

程序执行的速度比页面渲染速度快很多,因此之前的等待都是使用线程的强制等待,这并不合理。等待一共分为四种。
强制等待、隐式等待、显示等待、流畅等待

  1. 强制等待
    程序阻塞运行,Thread.sleep()。会用到,但是自动化里不能用的特别多,否则拖慢速度
  2. 隐式等待
    隐式等待会一直轮询判断元素是否存在,如果不存在就等待设置好的时间里不断地进行轮询知道元素能够被找到
private static void waitController(){
    String url = "https://www.baidu.com";
    ChromeDriver chromeDriver=new ChromeDriver();
    // 添加隐式等待:会作用于chromeDriver整个生命周期
    chromeDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
    chromeDriver.get(url);
    chromeDriver.findElement(new By.ByXPath("/html/body/div[1]/div[1]/div[5]/div/div/form/span[1]/input")).sendKeys("迪丽热巴");
    chromeDriver.findElement(new By.ByXPath("/html/body/div[1]/div[1]/div[5]/div/div/form/span[2]/input")).click();
    chromeDriver.findElement(new By.ByXPath("/html/body/div[2]/div[3]/div[1]/div[3]/div[1]/div[1]/div/div/div/div/div[2]/div/a/div/p/span/span"));
    chromeDriver.quit();
}
  1. 显示等待
    隐式等待针对的是 driver的整个生命周期,所以对全部元素有效,但有时候并非全部都需要等待,我们可以只针对其中一个元素进行等待操作,其余元素交给程序快速运行进而提高自动化测试的效率
private static void webDriverWait() {
    String url = "https://www.baidu.com";
    ChromeDriver chromeDriver = new ChromeDriver();
    chromeDriver.get(url);
    chromeDriver.findElement(new By.ByXPath("/html/body/div[1]/div[1]/div[5]/div/div/form/span[1]/input")).sendKeys("迪丽热巴");
    chromeDriver.findElement(new By.ByXPath("/html/body/div[1]/div[1]/div[5]/div/div/form/span[2]/input")).click();
    // 只针对某个元素显示等待
    new WebDriverWait(chromeDriver, Duration.ofSeconds(3)).until(lambda -> chromeDriver.findElement(new By.ByXPath("/html/body/div[2]/div[3]/div[1]/div[3]/div[1]/div[1]/div/div/div/div/div[2]/div/a/div/p/span/span")));
    chromeDriver.get(url);
}

这里使用了 lambda表达式 的语法,我们查看一下 until的源码
在这里插入图片描述
参数是一个泛型,所以可以传入任何泛型函数。然后就是执行的循环,获取页面元素操作。

  • 隐式等待 和 显式等待 并不能同时使用,可能会出现意想不到的结果
  • 隐式等待 和 显示等待 均不出现效果的时候可以使用 强制等待

浏览器的操作

浏览器的回退,前进和刷新操作

private static void navigateControl() {
    String url = "https://www.baidu.com";
    ChromeDriver chromeDriver = new ChromeDriver();
    // 简写
//        chromeDriver.get(url);
    // 非简写
    chromeDriver.navigate().to(url);
    chromeDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
    // 想要回退到访问百度网址之前的状态
    chromeDriver.navigate().back();
    // 前进,有进入到了百度首页
    chromeDriver.navigate().forward();
    // 刷新百度首页
    chromeDriver.navigate().refresh();
    chromeDriver.quit();
}

弹窗

弹窗一共有3大类型,如下所示分别为:alert、confirm和prompt
在这里插入图片描述
但是却发现,浏览器进入检查时候无法获取到弹窗的元素
处理弹窗的步骤

  1. 将driver对象作用到弹窗上(切换到弹窗)
  2. 选择确认/取消(提示弹窗输入文本)
    作用到弹窗上:driver.switchTo.alert()
    确认:driver.accept()
    取消:`driver.dismiss()
    输入:driver.sendKeys()

前端代码

<div id="alertVue">
    <button v-on:click="f()">{{value}}</button>
</div>
<div id="confirmVue">
    <button v-on:click="f()">{{value}}</button>
</div>
<div id="promptVue">
    <button v-on:click="f()">{{value}}</button>
</div>

<script type="text/javascript" src="vue.js"></script>
<script>
    const alertVue = new Vue({
        el: '#alertVue',
        data: {
            value: "Alert弹窗",
        },
        methods: {
            f() {
                window.alert("alert弹窗");
            }
        }
    });

    const confirmVue = new Vue({
        el: '#confirmVue',
        data: {
            value: "Confirm确认框",
        },
        methods: {
            f() {
                if (window.confirm("是否确认?")) {
                    confirmVue.value = "true";
                } else {
                    confirmVue.value = "false";
                }
            }
        }
    });
    const promptVue = new Vue({
        el: '#promptVue',
        data: {
            value: "输入框",
        },
        methods: {
            f() {
                promptVue.value = window.prompt("输入框");
            }
        }
    });
</script>

测试代码

private static void alertController(){
    String url = "file:///D:/Documents/Program/Java/SeleniumTest/Autotest/src/test/java/alertHTML.html";
    ChromeDriver chromeDriver=new ChromeDriver();
    chromeDriver.navigate().to(url);
    try {
        Thread.sleep(1500);
        // 点击 alert
        chromeDriver.findElement(new By.ByXPath("/html/body/div[1]/button")).click();
        Thread.sleep(1500);
        // 切换到弹窗
        Alert alert = chromeDriver.switchTo().alert();
        // 确认操作:accept/取消操作:dismiss
        alert.dismiss();
        Thread.sleep(1500);

        // 点击 confirm
        chromeDriver.findElement(new By.ByXPath("/html/body/div[2]/button")).click();
        Thread.sleep(1500);
        // 切换到弹窗
        alert = chromeDriver.switchTo().alert();
        // 确认操作
        alert.accept();
        Thread.sleep(1500);

        // 点击 prompt
        chromeDriver.findElement(new By.ByXPath("/html/body/div[3]/button")).click();
        Thread.sleep(1500);
        // 切换到弹窗
        alert = chromeDriver.switchTo().alert();
        Thread.sleep(1500);
        // 在页面上看不到输入的文本效果,但是其实已经输入了
        alert.sendKeys("阿斯顿马丁");
        // 接受文本
        alert.accept();
        Thread.sleep(1500);
        // 确认操作
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    chromeDriver.quit();
}
  • prompt 弹窗进行 sendKeys(str) 的时候不会在页面出现效果,但是程序已经输入有文本信息
  • alert 警告弹窗虽然只有确认选项,但是也可以用 dismiss和accept 都可以进行取消操作
  • url 一定要复制浏览器中选择而不是从资源路径中选择【浏览器的url前边会多一个file:///

选择器

选择框的处理困难在于无法将下拉框的元素进行定位,每当点击检查的时候,下拉框就会消失
这个时候我们可以根据三个条件进行选择

  1. 文本
  2. 属性
  3. 下标

html代码

<div id="selectVue">
    <select>
        <option v-for="(month, index) in monthes" v-bind:value="index+1">{{month}}</option>
    </select>
</div>

<script>
const selectVue = new Vue({
    el: '#selectVue',
    data: {
        monthes: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December",],
    },
});
</script>

Java代码

private static void alertController() {
    String url = "file:///D:/Documents/Program/Java/SeleniumTest/Autotest/src/test/java/alertHTML.html";
    ChromeDriver chromeDriver = new ChromeDriver();
    chromeDriver.navigate().to(url);
    try {
        // option选择器
        WebElement element = chromeDriver.findElement(new By.ByXPath("/html/body/div[4]/select"));
        Thread.sleep(1500);
        // 先创建选择框对象
        Select select = new Select(element);
        Thread.sleep(1500);
        // 根据文本来选择【并不会像正常操作一样点击一下选择框而是直接选中下拉框】
        select.selectByVisibleText("September");
        Thread.sleep(1500);
        // 根据属性值选择
        select.selectByValue("1");
        Thread.sleep(1500);
        // 根据下标选择【0开始】,选取九月
        select.selectByIndex(8);
        Thread.sleep(1500);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    chromeDriver.quit();
}

XPath的选择器是从1开始;而下标选择从0开始

执行脚本

执行 js 代码,有时候会遇到输入文本不完全或者不生效的情况下,可以使用执行原生js脚本进行解决

private static void scriptControl(){
    String url = "https://image.baidu.com/";
    ChromeDriver chromeDriver = new ChromeDriver();
    chromeDriver.navigate().to(url);
    try {
        // 执行 js 代码让页面置底
        chromeDriver.executeScript("document.documentElement.scrollTop = 500");
        Thread.sleep(1500);
        // 执行 js 代码让页面置顶
        chromeDriver.executeScript("document.documentElement.scrollTop = 0");
        Thread.sleep(1500);
        
        // 百度输入框输入指定文本
        chromeDriver.navigate().to("https://www.baidu.com/");
        chromeDriver.executeScript("var ss=document.querySelector('#kw'); ss.value='英雄钢笔'");
        Thread.sleep(1500);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }

    chromeDriver.quit();
}

文件上传

文件上传的难点在于当点击上传文件时的弹窗是系统自己的弹窗,并不是浏览器的弹窗。对于Selenium而言并不能控制系统。
在这里插入图片描述

private static void fileUploadControl(){
    String url = "file:///D:/Documents/Program/Java/SeleniumTest/Autotest/src/test/java/alertHTML.html";
    ChromeDriver chromeDriver = new ChromeDriver();
    chromeDriver.navigate().to(url);
    try {
        Thread.sleep(1500);
        chromeDriver.findElement(new By.ByXPath("/html/body/div[5]/input")).sendKeys("D:\\Documents\\Program\\Java\\SeleniumTest\\Autotest\\src\\test\\java\\alertHTML.html");
        Thread.sleep(1500);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    chromeDriver.quit();
}

这里运行完毕之后并不是把文件上传到服务器而是要把上传的文件路径+文件以名称的方式放到这里

浏览器参数

实际工作中,测试人员将自动化部署在机器上自动的执行,测试人员不会每次都一直盯着自动化执行的过程,而是直接查看自动化执行的结果。
无头模式:类似于Linux的&模式运行命令挂载在后台进程,不在前台显示。浏览器而言的话就是没有画面的运行一些自动化测试脚本,这样可以节约一定的机器内存资源。

private static void paramControl() {
    // 百度搜索 ”测试开发工程师“
    // 先创建选项对象然后设置浏览器参数
    String url = "https://www.baidu.com/";
    ChromeOptions chromeOptions = new ChromeOptions();
    chromeOptions.addArguments("-headless");
    // 添加浏览器参数设置
    ChromeDriver chromeDriver = new ChromeDriver(chromeOptions);
    chromeDriver.navigate().to(url);
    chromeDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
    chromeDriver.findElement(new By.ByXPath("/html/body/div[1]/div[1]/div[5]/div/div/form/span[1]/input")).sendKeys("测试开发工程师");
    chromeDriver.findElement(new By.ByXPath("/html/body/div[1]/div[1]/div[5]/div/div/form/span[2]/input")).click();
    chromeDriver.quit();
}

Junit 5

自动化是Selenium脚本来实现的,Junit是java的单元测试工具.只不过我们在实现自动化的时候需要借用Junit库里面提供的一些方法
Junit 5支持最低支持jdk8
目前Java领域内最为流行的单元测试框架 ------ JUnit
Junit 5 = Junit Platform + Junit Jupiter + Junit Vintage
Junit Platform: Junit Platform是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入
Junit Jupiter: Junit Jupiter提供了JUnit5的新的编程模型,是JUnit5新特性的核心。内部 包含了一个测试引擎,用于在Junit Platform上运行
Junit Vintage: 由于JUnit已经发展多年,为了照顾老的项目,JUnit Vintage提供了兼容JUnit4.x,Junit3.x的测试引擎
在这里插入图片描述

导入依赖

<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.9.2</version>
    <scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-suite -->
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-suite</artifactId>
    <version>1.9.2</version>
    <scope>test</scope>
</dependency>

Junit 4 和 Junit5 注解对比

Junit 5Junit 4说明
@Test@Test被注解的是一个测试方法,和Junit 4相同
@BeforeAll@BeforeClass被注解的(静态)方法将在当前类中的所有@Test方法执行一次
@BeforeEach@Before被注解的方法将在当前类中的么欸个@Test方法前执行
@AffterAll@AfterClass被注解的(静态)方法将在当前类中的所有@Test方法执行一次
@AffterEach@After被注解的方法将在当前类中的么欸个@Test方法后执行
@DIsable@Ignore被注解的方法不会执行(将被跳过),但会报告为已执行

Junit 4 中@Testorg.junit.Test;
Junit 5 中@Testorg.junit.jupiter.api.Test;

@BeforeAll 注解必须用在 static 修饰的代码块上,否则会报错

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class JunitTest {
    // 当前方法要在所有测试用例之前执行一次
    @BeforeAll
    static void beforeAll() {
        System.out.println("@BeforeAll");
    }

    // 当前方法要在每个测试用例执行之前执行一次
    @BeforeEach
    void beforeEach() {
        System.out.println("@BeforeEach");
    }

    @Test
    void t1() {
        System.out.println("@Test1");
    }

    @Test
    void t2() {
        System.out.println("@Test2");
    }

    @Test
    void t3() {
        System.out.println("@Test3");
    }
}

在这里插入图片描述
After效果和Before效果相反

import org.junit.jupiter.api.*;

public class JunitTest {
    // 当前方法要在所有测试用例之前执行一次
    @BeforeAll
    static void beforeAll() {
        System.out.println("@BeforeAll");
    }

    // 当前方法要在每个测试用例执行之前执行一次
    @BeforeEach
    void beforeEach() {
        System.out.println("@BeforeEach");
    }

    @Test
    void t1() {
        System.out.println("@Test1");
    }

    @Test
    void t2() {
        System.out.println("@Test2");
    }

    @Test
    void t3() {
        System.out.println("@Test3");
    }

    @AfterEach
    void afterEach() {
        System.out.println("@AfterEach");
    }

    @AfterAll
    static void afterAll() {
        System.out.println("@AfterAll");
    }
}

在这里插入图片描述

断言

断言方法说明
assertEquals(expected, actual)如果 expected 不等于 actual ,则断言失败
assertFalse(booleanExpression)如果 booleanExpression 不是 false ,则断言失败
assertNull(actual)如果 actual 不是 null ,则断言失败
assertNotNull(actual)如果 actual 是 null ,则断言失败
assertTrue(booleanExpression)如果 booleanExpression 不是 true ,则断言失败
@Test
void testAttributeValue() {
    String url = "https://www.baidu.com/";
    ChromeOptions chromeOptions = new ChromeOptions();
    chromeOptions.addArguments("-headless");
    ChromeDriver chromeDriver = new ChromeDriver(chromeOptions);
    chromeDriver.navigate().to(url);
    chromeDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
    String value = chromeDriver.findElement(By.cssSelector("#su")).getAttribute("value");
    // 假如这里获取到的属性值不是 百度一下 而是百度两下
    System.out.println("value:" + value);
    Assertions.assertEquals("百度两下", value);
    chromeDriver.quit();
}

断言失败
在这里插入图片描述
断言成功

@Test
void testAttributeValue() {
    String url = "https://www.baidu.com/";
    ChromeOptions chromeOptions = new ChromeOptions();
    chromeOptions.addArguments("-headless");
    ChromeDriver chromeDriver = new ChromeDriver(chromeOptions);
    chromeDriver.navigate().to(url);
    chromeDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
    String value = chromeDriver.findElement(By.cssSelector("#su")).getAttribute("value");
    // 假如这里获取到的属性值不是 百度一下 而是百度两下
    System.out.println("value:" + value);
    Assertions.assertNotEquals("百度两下", value);
    chromeDriver.quit();
}

在这里插入图片描述

不会提示错误地方

测试顺序

import org.junit.jupiter.api.*;

public class JunitTest {
    // 当前方法要在所有测试用例之前执行一次
    @BeforeAll
    static void beforeAll() {
        System.out.println("@BeforeAll");
    }

    // 当前方法要在每个测试用例执行之前执行一次
    @BeforeEach
    void beforeEach() {
        System.out.println("@BeforeEach");
    }

    @Test
    void ae() {
        System.out.println("@Test1");
    }

    @Test
    void bc() {
        System.out.println("@Test2");
    }

    @Test
    void ab() {
        System.out.println("@Test3");
    }

    @AfterEach
    void afterEach() {
        System.out.println("@AfterEach");
    }

    @AfterAll
    static void afterAll() {
        System.out.println("@AfterAll");
    }
}

@BeforeAll
@BeforeEach
@Test3
@AfterEach
@BeforeEach
@Test1
@AfterEach
@BeforeEach
@Test2
@AfterEach
@AfterAll

发现执行顺序按照方法名排序一样,这里的顺序并不是按照程序中代码的顺序执行。有时候我们需要按照顺序执行代码,比如测试系统的时候必须要用户先登陆才可以后续的一些操作的时候就需要按照顺序执行测试代码
添加一个 @TestMethodOrder 注解,然后添加参数 MethodOrderer.OrderAnnotation.class,最后再给每个执行的代码编辑顺序即可

import org.junit.jupiter.api.*;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class JunitTest {
    // 当前方法要在所有测试用例之前执行一次
    @BeforeAll
    static void beforeAll() {
        System.out.println("@BeforeAll");
    }

    // 当前方法要在每个测试用例执行之前执行一次
    @BeforeEach
    void beforeEach() {
        System.out.println("@BeforeEach");
    }

    @Test
    @Order(1)
    void ae() {
        System.out.println("@Test1");
    }

    @Test
    @Order(2)
    void bc() {
        System.out.println("@Test2");
    }

    @Test
    @Order(3)
    void ab() {
        System.out.println("@Test3");
    }

    @AfterEach
    void afterEach() {
        System.out.println("@AfterEach");
    }

    @AfterAll
    static void afterAll() {
        System.out.println("@AfterAll");
    }
}
@BeforeAll
@BeforeEach
@Test1
@AfterEach
@BeforeEach
@Test2
@AfterEach
@BeforeEach
@Test3
@AfterEach
@AfterAll

参数化

  1. 尽可能地通过一个用例,多组参数来模拟用户的行为
  2. 使用了参数化注解之后就不能再使用 @Test 注解

单参数

@ParameterizedTest// 使用参数化注解之前需要先声明该方法为参数化方法
@ValueSource(strings = {"1969612859@qq.com", "1969612858@qq.com"})// 通过注解提供数据源
void SingleParamsTest(String email){
    System.out.println(email);
}

在这里插入图片描述
可以看到 @ValueSource(数据类型={参数1, 参数2, 参数3...}) 提供的数据源类型有很多
在这里插入图片描述

多参数

@ParameterizedTest
@CsvSource({"1969612859@qq.com, admin", "1969612858@qq.com, root"})
void muchParamsTest(String email, String password){
    System.out.printf("email:%s<==>password:%s\n", email, password);
}

email:1969612859@qq.com<==>password:admin
email:1969612858@qq.com<==>password:root

当参数很多的时候,我们可以把参数放入文件中然后通过文件读取获取参数

@ParameterizedTest
@CsvFileSource(files = "D:\\Documents\\Program\\Java\\SeleniumTest\\Autotest\\src\\test\\resources\\userData.csv")
void csvFileParamsTest(String email, String password){
    System.out.printf("email:%s<==>password:%s\n", email, password);
}

在这里插入图片描述
发现 @CsvFileSource 的源码可以放入很多参数来设置文件,这里只放了 files 一个参数的值,文件默认编码已经是 UTF-8 所以就可以不用管编码问题
在这里插入图片描述

动态参数

这里导入一个随机工具类 common-util–使用方法

// 动态参数
static Stream<Arguments> methodParams(){
    // 构造动态参数
    String[] arr = new String[10];
    for (int i = 0; i < arr.length; i++) {
        arr[i] = RandomUtil.randomNumbers(10)+"@qq.com";
    }
    return Stream.of(
            Arguments.arguments(arr[0], RandomUtil.randomString(8)),
            Arguments.arguments(arr[1], RandomUtil.randomString(8)),
            Arguments.arguments(arr[2], RandomUtil.randomString(8)),
            Arguments.arguments(arr[3], RandomUtil.randomString(8)),
            Arguments.arguments(arr[4], RandomUtil.randomString(8)),
            Arguments.arguments(arr[5], RandomUtil.randomString(8)),
            Arguments.arguments(arr[6], RandomUtil.randomString(8)),
            Arguments.arguments(arr[7], RandomUtil.randomString(8)),
            Arguments.arguments(arr[8], RandomUtil.randomString(8)),
            Arguments.arguments(arr[9], RandomUtil.randomString(8))
    );
}
@ParameterizedTest
@MethodSource(value = "methodParams")
void dynamicMethodParams(String email, String password){
    System.out.printf("email:%s<==>password:%s\n", email, password);
}
email:0150592006@qq.com<==>password:5knqh9nd
email:6506175266@qq.com<==>password:jndm1vx6
email:4815105218@qq.com<==>password:9e6yaky2
email:5072613647@qq.com<==>password:56vjv9ff
email:1471676761@qq.com<==>password:0uq2mx9r
email:0637284991@qq.com<==>password:k5xcauzb
email:8646939279@qq.com<==>password:q9zltwfd
email:7903224405@qq.com<==>password:wrgn7fxr
email:2771169159@qq.com<==>password:f3l255bc
email:8080867273@qq.com<==>password:mnpveuxj

在这里插入图片描述

这里为了方便,就直接使用 Arguments 来替代

小技巧
如果数据源与测试方法同名,则无需执行数据源是来自哪里的

// 动态参数
static Stream<Arguments> dynamicMethodParams(){
    // 构造动态参数
    String[] arr = new String[10];
    for (int i = 0; i < arr.length; i++) {
        arr[i] = RandomUtil.randomNumbers(10)+"@qq.com";
    }
    return Stream.of(
            Arguments.arguments(arr[0], RandomUtil.randomString(8)),
            Arguments.arguments(arr[1], RandomUtil.randomString(8)),
            Arguments.arguments(arr[2], RandomUtil.randomString(8)),
            Arguments.arguments(arr[3], RandomUtil.randomString(8)),
            Arguments.arguments(arr[4], RandomUtil.randomString(8)),
            Arguments.arguments(arr[5], RandomUtil.randomString(8)),
            Arguments.arguments(arr[6], RandomUtil.randomString(8)),
            Arguments.arguments(arr[7], RandomUtil.randomString(8)),
            Arguments.arguments(arr[8], RandomUtil.randomString(8)),
            Arguments.arguments(arr[9], RandomUtil.randomString(8))
    );
}
@ParameterizedTest
@MethodSource// 自动寻找与方法名同名的数据源
void dynamicMethodParams(String email, String password){
    System.out.printf("email:%s<==>password:%s\n", email, password);
}
email:7264428849@qq.com<==>password:wewp75wh
email:4884099760@qq.com<==>password:q57n49kf
email:2779525807@qq.com<==>password:dkyfns0t
email:7260421546@qq.com<==>password:kfxow9gw
email:9750978471@qq.com<==>password:a5dwh5g4
email:7595861999@qq.com<==>password:g9gszroo
email:5860224630@qq.com<==>password:er521mby
email:7009711558@qq.com<==>password:qhftn0rh
email:9374899761@qq.com<==>password:zsjkkyns
email:6637232897@qq.com<==>password:fu8g79om

测试套件

指定类来运行测试用例

package TestSuite;

import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;

// 测试套件:通过 @Suite 注解
@Suite
@SelectClasses(value = {aaa.class, bbb.class})
public class runSuite {
}

测试套件测试 aaa.java, bbb.java, ccc.java 的测试结果,类下想要运行的用例必须要被 @Test 注解修饰
在这里插入图片描述
@Suite 注解标识该类是测试套件类而不是测试类
@SelectClasses 部分源码告知我们需要填上 class 类名
在这里插入图片描述

指定包名来运行包下测试用例

package TestSuite;

import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.SelectPackages;
import org.junit.platform.suite.api.Suite;

// 测试套件:通过 @Suite 注解
@Suite
//@SelectClasses(value = {aaa.class, bbb.class})
@SelectPackages("TestSuite")
public class runSuite {
}

发现连 ccc.java 的测试也打印输出了
在这里插入图片描述

最好是把要测试的文件以 xxxTest 结尾比较好,但是因为文件夹是 TestSuite的缘故,所以现在即使不是 xxxTest 结尾的包文件也能被扫描到并执行 @Test 注解的方法,否则就会执行报错。

报错问题如下所示
在这里插入图片描述
解决方案如下所示
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/385843.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【线程安全篇】

线程安全之原子性问题 x &#xff0c;在字节码文件中对应多个指令&#xff0c;多个线程在运行多个指令时&#xff0c;就存在原子性、可见性问题 赋值 多线程场景下&#xff0c;一个指令如果包含多个字节码指令&#xff0c;那么就不再是原子操作。因为赋值的同时&#xff0c…

智慧工地AI视频分析系统 opencv

智慧工地AI视频分析系统通过pythonopencv网络模型图像识别技术&#xff0c;智慧工地AI视频分析算法自动识别现场人员穿戴是否合规。本算法模型中用到opencv技术&#xff0c;OpenCV基于C实现&#xff0c;同时提供python, Ruby, Matlab等语言的接口。OpenCV-Python是OpenCV的Pyth…

研讨会回顾 | Perforce版本控制工具Helix Core入华十年,携手龙智赋能企业大规模研发

2023年2月28日&#xff0c;龙智联合全球领先的数字资产管理工具厂商Perforce共同举办Perforce on Tour网络研讨会&#xff0c;主题为“赋能‘大’研发&#xff0c;助力‘快’交付”。 作为Perforce Helix Core产品在中国地区的唯一授权合作伙伴&#xff0c;龙智董事长何明女士为…

六、GoF之工厂模式

设计模式&#xff1a;一种可以被重复利用的解决方案。 GoF&#xff08;Gang of Four&#xff09;&#xff0c;中文名——四人组。 《Design Patterns: Elements of Reusable Object-Oriented Software》&#xff08;即《设计模式》一书&#xff09;&#xff0c;1995年由 Eric…

Sidecar-详解 JuiceFS CSI Driver 新模式

近期发布的 JuiceFS CSI Driver v0.18 版本中&#xff0c;我们提供了一种全新的方式访问文件系统&#xff0c;即 JuiceFS 客户端以 Sidecar 方式运行于应用 Pod 中&#xff0c;且客户端与应用同生命周期。 这个全新的功能将帮助用户在 Serverless Kubernetes 环境中使用 Juice…

【Python每日一练】总目录(不断更新中...)

Python 2023.03 20230303 1. 两数之和 ★ 2. 组合总和 ★★ 3. 相同的树 ★★ 20230302 1. 字符串统计 2. 合并两个有序链表 3. 下一个排列 20230301 1. 只出现一次的数字 2. 以特殊格式处理连续增加的数字 3. 最短回文串 Python 2023.02 20230228 1. 螺旋矩阵 …

基于k3s部署KubeSphere

目录相关文档准备工作安装K3S安装KubeSphere相关文档 k3s官网&#xff1a;https://docs.k3s.io/zh/quick-start k3s所有版本查看&#xff1a;https://github.com/k3s-io/k3s/tags kubesphere文档&#xff1a;https://kubesphere.io/zh/docs/v3.3/quick-start/minimal-kubesp…

2023爱分析·RPA软件市场厂商评估报告:容智信息

目录 1. 研究范围定义 2. RPA软件市场分析 3. 厂商评估&#xff1a;容智信息 4. 入选证书 1. 研究范围定义 RPA即Robotic Process Automation&#xff08;机器人流程自动化&#xff09;&#xff0c;是一种通过模拟人与软件系统的交互过程&#xff0c;实现由软件机器人…

【python+selenium自动化测试实战项目】全面、完整、详细

今天整理一下实战项目的代码共大家学习。 不说废话&#xff0c;直接上项目 项目简介 项目名称&#xff1a;**公司电子零售会员系统 项目目的&#xff1a;实现电子零售会员系统项目自动化测试执行 项目版本&#xff1a;v1.0 项目目录 项目环境 本版 python 36 pip insat…

Linux开放的端口太多了?教你一招找出所有开放的端口,然后直接干掉!

基于服务器安全性维护的目的&#xff0c;查看所有开放的端口是通常采取的第一步&#xff0c;从中检查出可疑或者不必要的端口并将其关掉。关于查看开放的端口&#xff0c;方法不止一种&#xff0c;比如lsof 命令&#xff0c;还可以使用 ss 命令。 查看开放的端口 今天我们就介…

分布式缓存 Memcached Linux 系统安装

1.Memcached简介 Memcached是一个开源、高性能&#xff0c;将数据分布于内存中并使用key-value存储结构的缓存系统。它通过在内存中缓存数据来减少向数据库的频繁访问连接的次数&#xff0c;可以提高动态、数据库驱动之类网站的运行速度。 Memcached在使用是比较简单的&#…

Clip:学习笔记

Clip 文章目录Clip前言一、原理1.1 摘要1.2 引言1.3 方法1.4 实验1.4.1 zero-shot Transfer1.4.2 PROMPT ENGINEERING AND ENSEMBLING1.5 局限性二、总结前言 阅读论文&#xff1a; Learning Transferable Visual Models From Natural Language Supervision CLIP 论文逐段精读…

只需4步,让OKA40i-C开发板的Linux系统拥有中文显示

如果你试着在Linux系统里面输入中文&#xff0c;那么将会有一片乱码呈现在你面前&#xff0c;这是因为Linux系统的默认语言是英文。但是如果可以显示中文的话&#xff0c;那么在使用过程中的便利程度一定会大大提升。今天小编就通过飞凌嵌入式的OKA40i-C开发板来为大家演示让Li…

极狐GitLab DevSecOps 为企业许可证安全合规保驾护航

本文来自&#xff1a; 小马哥 极狐(GitLab) 技术布道师 开源许可证是开源软件的法律武器&#xff0c;是第三方正确使用开源软件的安全合规依据。 根据 Linux 发布的 SBOM 报告显示&#xff0c;98% 的企业都在使用开源软件&#xff08;中文版报告详情&#xff09;。随着开源使用…

微电影广告有哪些传播优势?

微电影广告是在基于微电影的模式下发展而来的&#xff0c;是伴随着当下快节奏、碎片化的生活方式而诞生的新兴广告表现形式。微电影广告凭借其具备的独特传播优势以及时代特征成为广大企业主塑造企业品牌形象的主要方式。那么&#xff0c;微电影广告究竟有哪些传播优势&#xf…

Ubuntu22.04安装、配置、美化、软件安装、配置开发环境

Ubuntu22.04安装、配置、美化、软件安装、配置开发环境 一、Ubuntu、Windows11&#xff08;10&#xff09;双系统安装 因为ubuntu的安装网上的教程特别多了&#xff0c;所以这里不做赘述&#xff0c;推荐使用小破站这个up主的教程&#xff1a;Windows 和 Ubuntu 双系统从安装到…

计算机系统的基本组成 第一节

一、计算机系统 计算机系统是指&#xff1a;电子数字通用、计算机系统 由硬件和软件两个子系统组成 硬件是保存和运行软件的物质基础 软件是指挥硬件完成预期功能的智力部分 重点&#xff1a; 计算机系统部件 五个 1、数据运算部件&#xff1a;完成对数据的运算处理功能…

Docker 配置WebSSH

1、基于DockerHub Centos镜像 docker pull centos:centos7 2、 Centos镜像存在的一个自身问题&#xff1a;启动后的容器内部无法使用systemctl命令 Failed to get D-Bus connection: Operation not permitted ## docker run -dit eeb6ee3f44bd /bin/bash ## 切勿忘记宿主机防…

【测绘程序设计】——平面坐标转换

测绘工程中经常遇到平面坐标转换——比如,北京54(或西安80)平面坐标转换成CGCS2000平面坐标、工程独立坐标系平面坐标转换成CGCS2000平面坐标等,常用转换模型包括:①三参数法(2平移+1旋转);②四参数法(赫尔默特法,2平移+1旋转+1尺度);③六参数法(仿射变换法,2平移…

00后看完这个直接拿下字节offer,软件测试100+道面试题

软件测试与质量保证-软件测试部分练习题 1单选(2分) 软件测试用例主要由输入数据和_________两部分组成。 A.预期输出结果2.00/2.00 B.测试计划 C.以往测试记录分析 D.测试规则 2单选(2分) 与设计测试用例无关的文档是_________。 A.项目开发计划2.00/2.00 B.源程序 …