基于Spring的forum系统自动化测试

唯有热爱,恒常如新

自动化测试

1. 测试用例

2. 代码编写

  • 根据测试用例进行UI自动化测试。
  • 公共属性单独放一个类,方便进行代码复用。
  • 注意添加隐式等待,确保页面正确加载显示。

2.1 添加依赖pom.xml

2.2 创建常用工具类AutoTestUtil

// 常用方法
public class AutoTestUtil {

    public static ChromeDriver driver = null;

    // 创建谷歌驱动对象
    public static ChromeDriver createDriverUtil() {

        // 驱动打开谷歌浏览器
        WebDriverManager.chromedriver().setup();
        // 配置安全策略
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--remote-allow-origins=*");

        // 无头模式, 可以后台执行, 观察效果就关闭
        // options.addArguments("--headless=new");

        // 将配置添加到浏览器对象中
        if (driver == null) {
            driver = new ChromeDriver(options);
        }

        // 添加 隐式等待 确保页面元素渲染出来
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
        return driver;
    }


    // 屏幕截图: 文件名带有类名和方法名
    public static void getScreenshotAs(String classMethodName) throws IOException {

        // 1. 用一个单独文件夹存放截图文件
        // 2. 出问题能够快速定位到(方便查找, 见名知意)
        SimpleDateFormat ymd = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat hms = new SimpleDateFormat("HH_mm_ss_SS");  // HH:mm:ss 文件名字不能带有 :, 所以使用 _

        String currentYMD = ymd.format(System.currentTimeMillis());
        String currentHMS = hms.format(System.currentTimeMillis());
        // 截图存储的路径
        String filePath = "./src/test/image/" + currentYMD + "/" + classMethodName + "_" +currentHMS + ".png";

        // 截图
        File screenshotAs = ( (TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
        File destFile = new File(filePath);

        // 保存到指定目录
        FileUtils.copyFile(screenshotAs, destFile);

    }

    // 获取调用者方法名
    public static String currentMethodName() {
        // 通过跟踪堆栈信息获取方法名
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        // 索引选择规则:
        // stackTrace[0] -> getStackTrace() 方法本身
        // stackTrace[1] -> 当前方法(currentMethodName)
        // stackTrace[2] -> 调用者方法(比如main)
        return stackTrace[2].getMethodName();

    }

}

2.3 注册页面测试

2.3.1 注册成功

注册用户名在数据库不存在,昵称,两次密码一致,注册成功,跳转到登录界面

预期结果: 注册成功, 跳转到登录界面

代码:

@Order(1)
    @ParameterizedTest
    @CsvSource({"zyt, 周诣涛, 123456, 123456"})
    void register(String username, String nickname, String password, String passwordRepeat) throws InterruptedException, IOException {
        driver.get("http://120.26.251.76:58080/sign-up.html");

        driver.findElement(By.cssSelector("#username")).sendKeys(username);
        driver.findElement(By.cssSelector("#nickname")).sendKeys(nickname);
        driver.findElement(By.cssSelector("#password")).sendKeys(password);
        driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys(passwordRepeat);

        // 点击同意用户协议
        driver.findElement(By.cssSelector("#policy")).click();
        // 点击注册
        driver.findElement(By.cssSelector("#submit")).click();
        Thread.sleep(1000);
        AutoTestUtil.getScreenshotAs(getClass().getName() + "_" + AutoTestUtil.currentMethodName());

    }

实际结果:与预期结果一致,注册功能测试通过

2.3.2 注册失败

注册用户名在数据库已经存在,昵称,两次密码一致,注册失败

预期结果: 提示当前用户已注册

代码:

@Order(1)
    @ParameterizedTest
    @CsvSource({"zyt, 周诣涛, 123456, 123456"})
    void register(String username, String nickname, String password, String passwordRepeat) throws InterruptedException, IOException {
        driver.get("http://120.26.251.76:58080/sign-up.html");

        driver.findElement(By.cssSelector("#username")).sendKeys(username);
        driver.findElement(By.cssSelector("#nickname")).sendKeys(nickname);
        driver.findElement(By.cssSelector("#password")).sendKeys(password);
        driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys(passwordRepeat);

        // 点击同意用户协议
        driver.findElement(By.cssSelector("#policy")).click();
        // 点击注册
        driver.findElement(By.cssSelector("#submit")).click();
        Thread.sleep(1000);
        AutoTestUtil.getScreenshotAs(getClass().getName() + "_" + AutoTestUtil.currentMethodName());

    }

实际结果: 与预期结果一致,右下角弹出弹窗显示“用户已存在”

2.4 登录页面测试

2.4.1 登录成功

测试用户名正确,密码正确,登录成功(多个用户)

预期效果: 两个用户都登录成功

代码:

@Order(1)
    @ParameterizedTest
    @CsvSource({"xxz, 123456", "zyt, 123456"})
    void login(String username, String password) throws IOException, InterruptedException {
        driver.get("http://120.26.251.76:58080/sign-in.html");

        driver.findElement(By.cssSelector("#username")).sendKeys(username);
        driver.findElement(By.cssSelector("#password")).sendKeys(password);
        driver.findElement(By.cssSelector("#submit")).click();
        // 截图, 查看是否登录成功, 等待一秒, 防止页面没加载出来就截图了
        Thread.sleep(1000);
        AutoTestUtil.getScreenshotAs(getClass().getName() + "_" + AutoTestUtil.currentMethodName());

    }

实际结果:与预期结果一致,登录功能测试通过

2.4.2 登录异常

测试用户名正确,密码错误,登录失败

测试用户名错误,密码错误,登录失败

预期结果: 提示用户名或密码错误

代码:

@Order(2)
    @ParameterizedTest
    @CsvSource({"zyt, 12345", "error1, error2"})
    void noLogin(String username, String password) throws IOException, InterruptedException {
        driver.get("http://120.26.251.76:58080/sign-in.html");
        driver.findElement(By.cssSelector("#username")).sendKeys(username);
        driver.findElement(By.cssSelector("#password")).sendKeys(password);
        driver.findElement(By.cssSelector("#submit")).click();
        Thread.sleep(1000);
        AutoTestUtil.getScreenshotAs(getClass().getName() + "_" + AutoTestUtil.currentMethodName());

    }

实际结果:与预期结果一致,右下角弹出弹窗显示“用户名或密码错误”

2.5 发布帖子测试

2.5.1 发布成功

登录后,输入帖子标题,不输入帖子内容,点击发布按钮,给出提示信息

预期结果: 提示: 请输入贴子内容

代码:

@Order(2)
    @ParameterizedTest
    @CsvSource({"xxz, 123456"})
    void writeActicleFailByLogin(String username, String password) throws IOException, InterruptedException {
        driver.get("http://120.26.251.76:58080/sign-in.html");
        driver.findElement(By.cssSelector("#username")).sendKeys(username);
        driver.findElement(By.cssSelector("#password")).sendKeys(password);
        driver.findElement(By.cssSelector("#submit")).click();

        driver.findElement(
                        By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div.col-auto.ms-auto.d-print-none > div > a.btn.btn-primary.d-none.d-sm-inline-block.article_post"))
                .click();

        String str1 = driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div.card-body > div:nth-child(1) > label > strong")).getText();
        // 断言判断是否处于发布贴子的页面
        Assertions.assertEquals("版块", str1);

        // 输入标题
        driver.findElement(By.cssSelector("#article_post_title")).sendKeys("标题");
        // 不输入内容, 直接发布, 查看是否提示: 请输入文章内容

        // 由于发布按钮需要向下滚动才能看到, 显示等待发布按钮加载
        WebElement element = driver.findElement(By.cssSelector("#article_post_submit"));
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));

        // 将指定元素滑动到页面顶部
        ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView();", element);
        // 确保滚动成功
        Thread.sleep(1000);
        element.click();

        Thread.sleep(1000);
        AutoTestUtil.getScreenshotAs(getClass().getName() + "_" + AutoTestUtil.currentMethodName());

    }

实际结果:与预期结果一致,发布帖子功能测试通过

登录后,输入帖子标题,输入帖子内容,点击发布按钮,发布成功

预期结果: 跳转回论坛主页, 且在上方显示贴子信息

代码:

@Order(3)
    @ParameterizedTest
    @CsvSource({"xxz, 123456"})
    void writeActicleSuccessByLogin(String username, String password) throws IOException, InterruptedException {

        // 直接在刚才的基础上测试
        driver.get("http://120.26.251.76:58080/sign-in.html");
        driver.findElement(By.cssSelector("#username")).sendKeys(username);
        driver.findElement(By.cssSelector("#password")).sendKeys(password);
        driver.findElement(By.cssSelector("#submit")).click();

        driver.findElement(
                        By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div.col-auto.ms-auto.d-print-none > div > a.btn.btn-primary.d-none.d-sm-inline-block.article_post"))
                .click();

        String str1 = driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div.card-body > div:nth-child(1) > label > strong")).getText();
        // 断言判断是否处于发布贴子的页面
        Assertions.assertEquals("版块", str1);

        // 输入标题
        driver.findElement(By.cssSelector("#article_post_title")).sendKeys("标题");

        // 输入内容, 鼠标通过tab要先移到输入框中
        Actions actions = new Actions(driver);
        actions.sendKeys(Keys.TAB)
                .sendKeys("测试内容")
                .perform();
        // 防止执行太快, 导致文本刚输入还没被渲染完毕就点击了
        Thread.sleep(1000);

        // 由于发布按钮需要向下滚动才能看到, 隐式等待发布按钮加载
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
        WebElement element = driver.findElement(By.cssSelector("#article_post_submit"));

        // 将指定元素滑动到页面顶部
        ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView();", element);
        Thread.sleep(1000);

        element.click();

        Thread.sleep(1000);
        AutoTestUtil.getScreenshotAs(getClass().getName() + "_" + AutoTestUtil.currentMethodName());

    }

实际结果: 与预期结果相符,发帖功能测试通过

2.5.2 发布失败

未登录状态下发布帖子,直接跳转回登录界面

预期结果: 直接跳转回登录界面

代码:

// 未登录状态访问论坛主页
    @Order(1)
    @Test
    void noLoginVisitWriteActicle() {
        driver.get("http://120.26.251.76:58080/index.html");
        String currentUrl = driver.getCurrentUrl();
        String signInUrl = "http://120.26.251.76:58080/sign-in.html";
        // 断言当前Url是否是登录页面的Url
        Assertions.assertEquals(currentUrl, signInUrl);
    }

实际结果: 与预期结果相符,跳转回登录页面

2.6 查看帖子测试

2.6.1 查看成功

页面基础元素: 贴子标题、贴子内容、贴子作者、点赞按钮

特殊元素: 如果是作者, 会多显示 编辑按钮 和 删除按钮

代码:

@Order(2)
    @ParameterizedTest
    @CsvSource({"xxz, 123456", "zyt, 123456"})
    void acticleDetailByLogin(String username, String password) throws IOException, InterruptedException {
        driver.get("http://120.26.251.76:58080/sign-in.html");
        driver.findElement(By.cssSelector("#username")).sendKeys(username);
        driver.findElement(By.cssSelector("#password")).sendKeys(password);
        driver.findElement(By.cssSelector("#submit")).click();

        // 断言进入到首页
        Assertions.assertEquals(driver.findElement(By.cssSelector("#article_list_board_title"))
                .getText(), "首页");

        // 进入到第一个贴子,如果页面上存在多个匹配相同定位条件的元素, 会默认返回第一个
        WebElement element = driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong"));
        element.click();

        // 断言进入了文章详情页面
        element = driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3"));
        Assertions.assertEquals(element.getText(), "最新回复");
        Thread.sleep(1000);
        AutoTestUtil.getScreenshotAs(getClass().getName() + "_" + AutoTestUtil.currentMethodName());

    }

场景1:登录后, 进入贴子详情界面, 且用户为贴子作者

预期结果: 多显示 编辑按钮 和 删除按钮

实际结果: 与预期结果相符

场景1执行后的页面

场景2:登录后, 进入贴子详情界面, 用户不为贴子作者

预期结果: 只显示 贴子标题、贴子内容、贴子作者、点赞按钮

实际结果: 与预期结果相符

场景2执行后的页面

2.6.2 查看失败

未登录状态下,进入贴子详情页,直接跳转回登录界面

预期结果: 直接跳转回登录界面

代码:

// 未登录状态访问论坛主页
    @Order(1)
    @Test
    void noLoginVisitWriteActicle() {
        driver.get("http://120.26.251.76:58080/index.html");
        String currentUrl = driver.getCurrentUrl();
        String signInUrl = "http://120.26.251.76:58080/sign-in.html";
        // 断言当前Url是否是登录页面的Url
        Assertions.assertEquals(currentUrl, signInUrl);
    }

实际结果: 与预期结果相符

2.7 删除帖子测试

2.7.1 删除成功

登录后,进入帖子详情页,点击删除按钮,删除帖子成功,成功跳转回帖子列表页面

注意:当前登录用户必须为贴子作者

预期结果: 提示删除成功

代码:

@Order(2)
    @ParameterizedTest
    @CsvSource({"xxz, 123456", "zyt, 123456"})
    void acticleDetailByLogin(String username, String password) throws IOException, InterruptedException {
        driver.get("http://120.26.251.76:58080/sign-in.html");
        driver.findElement(By.cssSelector("#username")).sendKeys(username);
        driver.findElement(By.cssSelector("#password")).sendKeys(password);
        driver.findElement(By.cssSelector("#submit")).click();

        // 断言进入到首页
        Assertions.assertEquals(driver.findElement(By.cssSelector("#article_list_board_title"))
                .getText(), "首页");

        // 进入到第一个贴子,如果页面上存在多个匹配相同定位条件的元素, 会默认返回第一个
        WebElement element = driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong"));
        element.click();

        // 断言进入了文章详情页面
        element = driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3"));
        Assertions.assertEquals(element.getText(), "最新回复");
        Thread.sleep(1000);
        AutoTestUtil.getScreenshotAs(getClass().getName() + "_" + AutoTestUtil.currentMethodName());

    }

实际结果: 与预期结果相符

2.7.2 删除失败

未登录状态下,点击删除

预期结果: 直接跳转回登录界面

代码:

// 未登录状态访问论坛主页
    @Order(1)
    @Test
    void noLoginVisitWriteActicle() {
        driver.get("http://120.26.251.76:58080/index.html");
        String currentUrl = driver.getCurrentUrl();
        String signInUrl = "http://120.26.251.76:58080/sign-in.html";
        // 断言当前Url是否是登录页面的Url
        Assertions.assertEquals(currentUrl, signInUrl);
    }

实际结果: 与预期结果相符

2.8 个人简介测试

2.8.1 修改成功

登录后点击用户头像下的个人中心,输入新昵称.....并修改,观察是否修改成功

预期结果: 弹窗提示修改昵称成功

代码: 

@Order(3)
    @ParameterizedTest
    @CsvSource({"xxz, 123456, 九尾www"})
    void updateUserMessageByLogin(String username, String password, String nickName) throws IOException, InterruptedException {
        driver.get("http://120.26.251.76:58080/sign-in.html");
        driver.findElement(By.cssSelector("#username")).sendKeys(username);
        driver.findElement(By.cssSelector("#password")).sendKeys(password);
        driver.findElement(By.cssSelector("#submit")).click();

        driver.findElement(By.xpath("/html/body/div[1]/header[1]/div/div/div[3]/a")).click();
        driver.findElement(By.cssSelector("#index_user_settings")).click();

        // 先清空
        driver.findElement(By.cssSelector("#setting_input_nickname")).clear();
        driver.findElement(By.cssSelector("#setting_input_nickname")).sendKeys(nickName);
        driver.findElement(By.cssSelector("#setting_submit_nickname")).click();

        Thread.sleep(1000);
        AutoTestUtil.getScreenshotAs(getClass().getName() + "_" + AutoTestUtil.currentMethodName());

    }

实际结果: 与预期结果相符

2.8.2 修改失败

未登录状态下,点击个人中心

预期结果: 直接跳转回登录界面

代码:

// 未登录状态访问论坛主页
    @Order(1)
    @Test
    void noLoginVisitWriteActicle() {
        driver.get("http://120.26.251.76:58080/index.html");
        String currentUrl = driver.getCurrentUrl();
        String signInUrl = "http://120.26.251.76:58080/sign-in.html";
        // 断言当前Url是否是登录页面的Url
        Assertions.assertEquals(currentUrl, signInUrl);
    }

实际结果: 与预期结果相符

实现方式及亮点

1. 实现方式: 

① 是根据个人项目来设计的测试用例,然后根据测试用例使用selenium4自动化测试工具

    Junit5单元测试框架结合来实现web自动化测试的(功能、步骤、技术一定要明确)

② 对于代码中的每个包都要进行概要介绍(公共属性[复用]、测试用例[根据每个页面来进行设计的],然后使用测试套件将所有测试类进行加载)

2. 亮点:

① 使用了JUnit5中提供的注解:避免生成过多的对象,造成资源和时间的浪费,提高了自动化的执行效率。
② 只创建一次驱动对象,避免每个用例重复创建驱动对象造成时间和资源的浪费。
③ 使用参数化:保持用例的简洁,提高代码的可读性
④ 使用测试套件:降低了测试人员的工作量,通过套件一次执行所有要运行的测试用例。
⑤ 使用了等待:提高了自动化的运行效率,提高了自动化的稳定性,减小误报的可能性。
⑥ 使用了屏幕截图:方便问题的追溯以及问题的解决。
⑦ 使用了无头模式:只注重结果,可以留出屏幕。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值