自学内容网 自学内容网

【测试项目】——个人博客系统自动化测试

📖 前言:本文针对个人博客项目进行测试,个人博客主要由四个页面构成:登录页、列表页、详情页和编辑页,主要功能包括:登录、编辑并发布博客、查看详情、删除博客以及注销等功能。对于个人博客的测试就是针对主要功能进行测试,然后按照页面书写测试类。


🕒 1. 博客系统页面概览

登录页:

在这里插入图片描述

列表页:

在这里插入图片描述

详情页:

在这里插入图片描述

编辑页:

在这里插入图片描述

🕒 2. 实施流程

自动化项目实施的基本流程:

  1. 熟悉项目
  2. 设计手工测试用例
  3. 手工测试用例转换成自动化测试用例
  4. 部署

请添加图片描述

🕒 3. 编写思路

  • 依据思维导图编写测试用例:为每个页面创建一个测试类,并在各个测试类中编写测试用例。
  • 公共属性应单独归类以便代码复用。
  • 运用测试套件以简化运行和修改过程。
  • 由于启动和现场截图功能会被频繁复用,应单独建立一个类来存放这些功能。
  • 添加隐式等待以确保页面能正确完全地加载。

🕘 3.1 添加相关依赖

创建Maven空项目后,在pom.xml里添加需要的依赖

<dependencies>
    <!--   添加selenium依赖  -->
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.24.0</version>
    </dependency>

    <!--   保存屏幕截图需要用到的包  -->
    <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.16.1</version>
    </dependency>

    <!--   添加junit5依赖  -->
    <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.10.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-params</artifactId>
        <version>5.10.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-suite -->
    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-suite</artifactId>
        <version>1.11.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.10.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

🕘 3.2 包结构

在这里插入图片描述

🕒 4. 代码编写

🕘 4.1 公共类AutoTestUtils

功能:创建驱动、保存现场截图

注意:在保存现场截图时,应按日期对文件夹进行分类,并确保图片名称反映出测试类别的名称,以便于问题追踪。同时,要留意文件名的动态生成和时间格式的设定。

public class AutoTestUtils {
    public static WebDriver webDriver;

    @BeforeAll
    static void SetUp() {
        if(webDriver == null)
        {
            webDriver = new ChromeDriver();
        }
        // 设置隐式等待时间为3秒
        webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
    }


    public static void TearDown() {
        webDriver.quit();
    }

    // 保存截图的方法
    public static void saveScreenshot(String testName) {
        // 获取当前时间并格式化
        String timestamp = new SimpleDateFormat("yyyyMMdd-HHmmssSSS").format(new Date());
        String dateFolder = new SimpleDateFormat("yyyyMMdd").format(new Date());
        String fileName = String.format("%s-%s.png", testName, timestamp);

        // 创建文件夹
        File folder = new File("screenshots/" + dateFolder);
        if (!folder.exists()) {
            folder.mkdirs(); // 创建文件夹
        }

        // 保存截图
        File screenshot = ((TakesScreenshot) webDriver).getScreenshotAs(OutputType.FILE);
        try {
            Files.copy(screenshot.toPath(), Paths.get(folder.getPath(), fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

🕘 4.2 BlogLoginTest(登录页测试)

  1. 创建驱动并打开网页。
  2. 检查网页是否正常加载。
  3. 进行正常登录测试:多参数测试,使用Enter键登录。
  4. 进行异常登录测试:用户名或密码错误,以及为空的情况。
  5. 使用Order注解确保测试按正确顺序执行,避免因顺序错误导致测试失败。
  6. 确保在重新输入用户名和密码前清空之前的内容。
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)

public class BlogLoginTest extends AutoTestUtils {
    // 所有用例都需要先获取登录页面
    @BeforeAll
    static void baseControl() {
        webDriver.get("http://localhost:8080/blog_system/login.html");
    }

    /**
     * 检查登录页面是否正确
     * 右上角与左上角的显示、登录框等
     */

    @Test
    @Order(1)
    void loginPageLoadRight() throws IOException {
        webDriver.findElement(By.cssSelector("body > div.nav > a:nth-child(4)"));
        webDriver.findElement(By.xpath("/html/body/div[1]/a[2]"));
        webDriver.findElement(By.cssSelector("body > div.login-container > form > div"));
        saveScreenshot(getClass().getName());
    }

    /**
     * 检查登录正常情况:使用多参数测试
     */

    @Order(3)
    @ParameterizedTest
    @CsvFileSource(resources = "LoginSuccess.csv")
    void loginSuccess(String username, String password, String blog_list_url) throws IOException {
        // 在每次登录之后都要进行清空,然后才能重新输入
        webDriver.findElement(By.cssSelector("#username")).clear();
        webDriver.findElement(By.cssSelector("#password")).clear();

        webDriver.findElement(By.cssSelector("#username")).sendKeys(username);
        webDriver.findElement(By.cssSelector("#password")).sendKeys(password);
        webDriver.findElement(By.cssSelector("#login-button")).sendKeys(Keys.ENTER);
        saveScreenshot(getClass().getName());
        webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);

        // 以上是登录步骤,但是并不能确保就是登录成功的
        // 获取到当前页面的URL,如果URL匹配则测试通过
        String cur_url = webDriver.getCurrentUrl();
        Assertions.assertEquals(blog_list_url, cur_url);

        // 列表页展示的用户信息是否是登录账号
        String cur_usr = webDriver.findElement(By.cssSelector("body > div.container > div.container-left > div > h3")).getText();
        Assertions.assertEquals(username, cur_usr);
        // 因为要多参数,所以在执行完一遍执行下一遍的时候需要进行页面的回退,否则找不到登录框
        webDriver.navigate().back();
    }

    /**
     * 测试完成后登录以便进行下一阶段测试
     */

    @Order(4)
    @Test
    void loginSuccessAfter()  {
        webDriver.findElement(By.cssSelector("#login-button")).click();
        webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
    }

    /**
     * 检查登录异常情况:使用多参数测试
     */

    @Order(2)
    @ParameterizedTest
    @CsvSource({"admin,123", "lisi,12", "'',''"})       // 第三项为空情况
    void loginFail(String username, String password) throws IOException {
        // 在每次登录之后都要进行清空,然后才能重新输入
        webDriver.findElement(By.cssSelector("#username")).clear();
        webDriver.findElement(By.cssSelector("#password")).clear();

        webDriver.findElement(By.cssSelector("#username")).sendKeys(username);
        webDriver.findElement(By.cssSelector("#password")).sendKeys(password);
        webDriver.findElement(By.cssSelector("#login-button")).click();
        saveScreenshot(getClass().getName());
        webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);

        // 登录失败的检测,获取文本进行比对
        String expectNotNull = "为空"; // 为空字样
        String expectError = "错误"; // 错误字样
        String actual = webDriver.findElement(By.cssSelector("body")).getText();

        if (actual.contains(expectNotNull) ||  actual.contains(expectError)) {
            System.out.println("登录失败测试通过");
        } else {
            System.out.println("登录失败测试不通过");
        }

        // 导航回登录页
        webDriver.navigate().back();
    }
}

🕘 4.3 BlogListTest(列表页测试)

  1. 检验博客列表页面是否能够正常打开。
  2. 验证列表页面的“查看全文”按钮是否能正确跳转。
  3. 检查未登录时的直接链接是否会重定向到登录页面。
public class BlogListTest extends AutoTestUtils {

    @BeforeAll
    static void baseControl()  {
        webDriver.get("http://localhost:8080/blog_system/blog_list.html");
    }

    /*
     * 博客列表页可以正常显示
     */
    @Test
    void listPageLoadRight() throws IOException {
        // 可以多检查几个,确保正确
        webDriver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)"));
        webDriver.findElement(By.cssSelector("body > div.container > div.container-left > div > img"));
        // 获取页面上所有博客标题对应的元素
        webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
        int title_num = webDriver.findElements(By.cssSelector(".title")).size();
        // 如果元素数量不为0,则测试通过
        Assertions.assertNotEquals(0,title_num);
        saveScreenshot(getClass().getName());
    }
}

🕘 4.4 BlogEditTest(编辑页测试)

  1. 检验编辑页面是否能正确打开
  2. 检查博客发布是否正常:元素是否完整或部分缺失
  3. 验证“写博客”按钮的功能性
public class BlogEditTest extends AutoTestUtils {
    @BeforeAll
    static void baseControl()  {
        webDriver.get("http://localhost:8080/blog_system/blog_list.html");
    }

    /*
     * 博客编辑页可以正常显示
     */

    @Test
    @Order(1)
    void editPageLoadRight() throws IOException {
        // 找到写博客按钮,点击
        webDriver.findElement(By.cssSelector("body > div.nav > a:nth-child(5)")).click();
        webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
        // 可以多检查几个,确保正确
        webDriver.findElement(By.cssSelector("#submit"));
        webDriver.findElement(By.cssSelector("#blog-title"));
        saveScreenshot(getClass().getName());
    }

    /*
     * 正确编辑并发布博客测试
     */

    @Test
    @Order(2)
    void editAndSubmitBlog() throws IOException {
        // 通过JS输入标题
        ((JavascriptExecutor) webDriver).executeScript("document.getElementById(\"blog-title\").value=\"自动化测试\"");
        // 编辑页的md是第三方插件,所以不可以直接使用sendKeys向编辑模块写入内容,但是可以通过点击上方按钮进行内容的插入
        webDriver.findElement(By.cssSelector("#editor > div.editormd-toolbar > div > ul > li:nth-child(30) > a > i")).click();
        webDriver.findElement(By.cssSelector("#editor > div.editormd-toolbar > div > ul > li:nth-child(5) > a > i")).click();
        saveScreenshot(getClass().getName());
        // 点击发布
        webDriver.findElement(By.cssSelector("#submit")).click();
        webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
        // 获取当前页面的URL
        String cur_url = webDriver.getCurrentUrl();
        Assertions.assertEquals("http://localhost:8080/blog_system/blog_list.html", cur_url);
        saveScreenshot(getClass().getName());
    }
}

🕘 4.5 BlogDetailTest(详情页测试)

  1. 正确打开测试详情页:分别测试有blogId和无blogId的情况
  2. 测试删除按钮是否可用,应比较时间而非标题,因为标题可能为空
  3. 注意操作时要确保能够导航回列表页
public class BlogDetailTest extends AutoTestUtils {
    @BeforeAll
    static void baseControl()  {
        webDriver.get("http://localhost:8080/blog_system/blog_list.html");
    }

    public static Stream<Arguments> Generator() {
        return Stream.of(Arguments.arguments("http://localhost:8080/blog_system/blog_detail.html",
                "博客详情页","自动化测试"));
    }

    @ParameterizedTest
    @MethodSource("Generator")
    void BlogDetail(String expected_url,String expected_title,String expected_blog_title) {
        // 找到第一个博客对应的查看全文按钮
        webDriver.findElement(By.xpath("/html/body/div[2]/div[2]/div[1]/a")).click();
        // 获取当前页面的URL、页面title、博客标题
        String cur_url = webDriver.getCurrentUrl();
        String cur_title = webDriver.getTitle();
        String cur_blog_title = webDriver.findElement(By.cssSelector("body > div.container > div.container-right > div > h3")).getText();
        Assertions.assertEquals(expected_title, cur_title);
        Assertions.assertEquals(expected_blog_title, cur_blog_title);
        if (cur_url.contains(expected_url)) {
            System.out.println("测试通过");
        } else {
            System.out.println("测试不通过");
        }
        webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
        saveScreenshot(getClass().getName());
    }
}

🕘 4.6 BlogDeleteTest(博客删除测试)

  1. 测试博客是否真正删除了
  2. 尝试删除别人的文章
  3. 删除完返回列表页
public class BlogDeleteTest extends AutoTestUtils {
    @BeforeAll
    static void baseControl()  {
        webDriver.get("http://localhost:8080/blog_system/blog_list.html");
    }

    @Test
    void DeleteBlog () {
        // 点击查看全文按钮
        webDriver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > a")).click();
        // 点击删除按钮
        webDriver.findElement(By.cssSelector("#delete_button")).click();
        // 博客列表页第一篇博客不是“自动化测试”
        String first_blog_title = webDriver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > div.title")).getText();
        // 校验当前博客标题不等于“自动化测试”
        Assertions.assertNotEquals(first_blog_title,"自动化测试");
        webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
        saveScreenshot(getClass().getName());

        // 尝试删除别人的文章
        // 点击查看全文按钮
        webDriver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > a")).click();
        // 点击删除按钮
        webDriver.findElement(By.cssSelector("#delete_button")).click();
        // 删除失败的检测,获取文本进行比对
        String expectError = "没有权限"; // 没有权限错误字样
        String actual = webDriver.findElement(By.cssSelector("body")).getText();

        if (actual.contains(expectError)) {
            System.out.println("删除失败测试通过");
        } else {
            System.out.println("删除失败测试不通过");
        }
        saveScreenshot(getClass().getName());
        webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
        // 导航回列表页
        webDriver.navigate().back();
    }
}

🕘 4.7 BlogLogoutTest

  1. 测试注销后是否返回登录页
  2. 断开驱动连接
public class BlogLogoutTest extends AutoTestUtils {
    @BeforeAll
    static void baseControl()  {
        webDriver.get("http://localhost:8080/blog_system/blog_list.html");
    }

    @Test
    void Logout(){
        webDriver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)")).click();
        // 校验当前页面URL是否是“http://localhost:8080/blog_system/login.html”
        String cur_url=webDriver.getCurrentUrl();
        Assertions.assertEquals("http://localhost:8080/blog_system/login.html",cur_url);
        // 校验提交按钮
        WebElement webElement = webDriver.findElement(By.cssSelector("#login-button"));
        Assertions.assertNotNull(webElement);
        webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
        saveScreenshot(getClass().getName());
        //webDriver.quit();
        TearDown();
    }
}

🕘 4.8 runSuite(测试套件)

测试套件按照测试类的顺序执行。

@Suite
@SelectClasses({BlogLoginTest.class, BlogListTest.class, BlogEditTest.class, BlogInfoChecked.class, BlogDetailTest.class, BlogDeleteTest.class, BlogLogoutTest.class})
public class runSuite {
}

🔎 自动化测试完整源码+博客项目

🕒 5. 测试结果

实际效果(无倍速):
请添加图片描述

在这里插入图片描述

🕒 6. 小结

  1. 关注测试用例的执行顺序
  2. 检查页面元素是否存在,以确保正确性。
  3. 注意多参数测试中的页面导航问题。
  4. 多参数(多用户登录)可能导致高并发服务器错误,需交由开发处理。
  5. 登录页面测试的最后一步应为登录成功状态,以便后续测试顺利进行。
  6. 驱动关闭应在最后一个用例结束后进行。
  7. 在公共类中定义保存截图方法以保存所有用例的执行结果,便于后续查错。截图保存时使用动态时间戳,按照天或周分类保存。
  8. @SelectClasses可指定执行类的顺序。
  9. 建议获取固定元素(如时间、标题)时,使用 XPath 定位。因为 XPath 可以基于元素的层级关系和属性进行查找,所以它适合用于固定元素的定位,特别是在复杂的 DOM 结构中。
  10. 可以适当关注用例执行时间,排查是否为性能问题。
  11. 可创建单独类存放驱动释放方法,放在套件测试的最后。
  12. 测试用例数量并非越多越好。
  13. 可以使用无头模式创建驱动。

【面试题】使用Selenium 4 自动化测试工具和JUnit 5 单元测试框架结合,如何实现的,以及有什么亮点?

1)实施:
① 设计测试用例是基于个人项目的需求,通过结合Selenium 4自动化测试工具和JUnit 5单元测试框架来执行Web自动化测试(功能、步骤和技术必须明确)。
② 需要对代码中的每个包提供概要介绍(公共属性[可复用]、测试用例[基于每个页面设计]),并使用测试套件来加载所有测试类。

2)特点:
① 利用JUnit 5提供的注解,避免创建过多对象,节省资源和时间,提升自动化测试的执行效率。
只创建一次驱动对象,减少每个测试用例重复创建驱动对象,节约时间和资源。
③ 采用参数化测试,使测试用例保持简洁,提高代码可读性。
④ 通过测试套件,减少了测试人员的工作量,一次性执行所有测试用例。
⑤ 使用隐式等待机制,确保页面加载完成,提升了自动化测试的运行效率和稳定性,减少了误报的可能性。
⑥ 采用屏幕截图功能,便于问题追踪和解决。
⑦ 使用无头模式,专注于测试结果,节省屏幕空间。

编写自动化测试不难,但要提高执行速度、更有效地发现问题并避免误报才是挑战。


OK,以上就是本期知识点“个人博客系统测试”的知识啦~~ ,感谢友友们的阅读。后续还会继续更新,欢迎持续关注哟📌~
💫如果有错误❌,欢迎批评指正呀👀~让我们一起相互进步🚀
🎉如果觉得收获满满,可以点点赞👍支持一下哟~

❗ 转载请注明出处
作者:HinsCoder
博客链接:🔎 作者博客主页


原文地址:https://blog.csdn.net/HinsCoder/article/details/142187278

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!