当前位置: 首页 > news >正文

校园论坛系统Selenium自动化测试

本文为自动化测试

 本项目自动化测试代码链接(仅供参考):  自动化测试代码

功能测试文章链接: 校园论坛系统自动化测试报告-CSDN博客

 🌈自动化测试

思维导图

根据思维导图, 我们选取几个主要的功能进行自动化测试

编写代码

思路: 

  1. 根据脑图进行测试用例的编写:每个页面一个测试类,然后再各个测试类中进行测试用例的编写
  2. 注意公共属性需要单独放一个类,方便进行代码复用
  3. 使用测试套件便于运行以及修改
  4. 创建启动以及现场截图就是会频繁进行复用,所以单独创建一个类进行存储
  5. 注意添加隐式等待,为了确保页面正确加载显示

💨准备工作

引入相关依赖

        <!--  屏幕截图  -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>

        <!--   驱动管理     -->
        <dependency>
            <groupId>io.github.bonigarcia</groupId>
            <artifactId>webdrivermanager</artifactId>
            <version>5.8.0</version>
        </dependency>

        <!--    selenium    -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>4.0.0</version>
        </dependency>

⭐创建常用工具类(AutoTestUtil)

工具类主要包含两个方法: 

1. 创建驱动对象: 测试过程会频繁创建和销毁, 可以封装成一个方法, 方便使用
2. 屏幕截图: 测试过程可能会出现问题, 可以使用屏幕截图保存问题界面, 便于后面分析

    要求: 图片文件名要做到: 见文知意, 方便问题追溯, 具体到 年月日时分秒, 以及方法名

3. 获取当前方法名: 通过跟踪堆栈信息获取调用者的方法名

下面是参考代码: 

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();

    }

}

每个页面的测试方法都使用一个类存放

⭐测试注册界面(ForumRegisterTest)

① 创建驱动,并打开注册页面
② 测试页面是否正常打开
③ 测试正常注册:多参数测试
④ 测试异常注册:密码和确认密码不一致的情况(此处不测null)
⑤ 注意测试方法的执行顺序,使用Order注解指定执行顺序

验证注册成功的情况

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

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

代码如下: 

    @Order(1)
    @ParameterizedTest
    @CsvSource({"Shylock, 夏洛克, 123456, 123456"})
    void register(String username, String nickname, String password, String passwordRepeat) throws InterruptedException, IOException {
        driver.get("http://127.0.0.1:11451/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:注册用户名在数据库存在,昵称,两次密码一致,注册失败

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

代码如下: 测试代码和注册成功的代码一样
✅实际结果: 与预期结果相符

⭐测试登录界面(ForumLoginTest)

测试点:

① 创建驱动,并打开页面
② 测试页面是否正常打开
③ 测试正常登录:多参数测试
④ 测试异常登录:用户名/密码错误的情况(此处不测null)
⑤ 注意测试的顺序,使用Order注解指定,否则可能会因为执行顺序不对导致测试失败
⑥ 注意清空内容后才能再次输入用户名以及密码

登录成功测试

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

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

代码如下: 

    @Order(1)
    @ParameterizedTest
    @CsvSource({"zhangsan, 123456", "2, 2"})
    void login(String username, String password) throws IOException, InterruptedException {
        driver.get("http://127.0.0.1:11451/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:测试用户名正确,密码错误,登录失败

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

☑️预期结果: 提示用户名或者密码错误和用户不存在

代码如下: 

    @Order(2)
    @ParameterizedTest
    @CsvSource({"zhangsan, 12345", "error1, error2"})
    void noLogin(String username, String password) throws IOException, InterruptedException {
        driver.get("http://127.0.0.1:11451/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());

    }

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

⭐测试论坛主页(ForumIndexTest)

① 测试论坛主页是否可以正常打开
② 测试未登录的直接链接是否会跳转到登录页面
③ 查看“首页”元素是否存在,查看是否含有“发布帖子”按钮
④ 同样注意执行顺序

❌未登录状态下测试

场景1:未登录状态下进入论坛主页,直接跳转回登录界面

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

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

代码如下: 

    // 未登录状态访问论坛主页
    @Order(1)
    @Test
    void noLoginVisitIndex() {
        driver.get("http://127.0.0.1:11451/index.html");
    }

✅登录状态下测试

场景2:登录后进入博客列表页,查看“首页”元素是否存在,查看是否含有“发布帖子”按钮

☑️预期结果: 在论坛主页可以找到对应的选择器或XPath, 点击发布贴子会跳转到贴子创作页

代码如下: 

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

        String str1 = driver.findElement(By.cssSelector("#nav_board_index > a > span.nav-link-title")).getText();
        // 断言 "首页" 元素存在
        Assertions.assertEquals("首页", str1);

        // 发布按钮, 点击会跳转到贴子发布页面
        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();
        // 截图, 查看是否跳转成功, 等待一秒, 防止页面没加载出来就截图了
        Thread.sleep(1000);
        AutoTestUtil.getScreenshotAs(getClass().getName() + "_" + AutoTestUtil.currentMethodName());

    }

✅实际结果: 断言通过, 也找到了发布按钮

⭐测试发布帖子(writePostTest)

测试要点: 

① 测试发布页是否可以正确打开
② 测试是否可以正常发布:元素齐全 or 部分元素
③ 测试“发布”按钮是否可以正常使用
④ 执行顺序

未登录状态下测试

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

和前面类似, 但是测试用例不能少, 由于篇幅限制就不多演示了

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

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

登录后测试

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

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

代码如下: 

    @Order(2)
    @ParameterizedTest
    @CsvSource({"zhangsan, 123456"})
    void writeActicleFailByLogin(String username, String password) throws IOException, InterruptedException {
        driver.get("http://127.0.0.1:11451/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());

    }

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

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

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

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

代码直接在前面的测试的方法上加上输入文本的操作即可

注意: editor编辑器不能通过选择选择到, 可以使用 键盘Tab 进行选择, 然后sendKeys即可

⭐测试贴子详情页(ActicleDetailTest)

① 测试详情页的正确打开
② 测试作者和用户权限是否一致

③ 功能是否正常
④ 执行顺序
⑤ 一定要注意导航回到列表页的操作

未登录状态下

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

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

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

和前面类似, 但是测试用例不能少, 由于篇幅限制就不多演示了

登录状态下

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

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

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

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

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

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

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

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

两个用例一块测试了

场景1执行后的页面

场景2执行后的页面

测试代码如下: 

    @Order(2)
    @ParameterizedTest
    @CsvSource({"2, 2", "zhangsan, 123456"})
    void acticleDetailByLogin(String username, String password) throws IOException, InterruptedException {
        driver.get("http://127.0.0.1:11451/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:登录后,进入帖子详情页,点击删除按钮,删除帖子成功,成功跳转回帖子列表页面

前提是当前登录用户为贴子作者

☑️预期结果: 提示删除成功

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

测试代码如下: 在上一个测试用例后面加上这串代码即可

        element = driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div:nth-child(1) > div.col-9.card.card-lg > div.card-footer.bg-transparent.mt-auto.justify-content-end > div > div:nth-child(3) > div > a > span"));
        // 断言当前用户是作者
        Assertions.assertEquals(element.getText(), "删除");
        element.click();
        driver.findElement(By.cssSelector("#details_artile_delete")).click();


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

⭐测试所有帖子页面

❌未登录测试

场景1: 未登录状态下,点击我的帖子

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

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

和前面类似, 但是测试用例不能少, 由于篇幅限制就不多演示了

✅登录状态下测试

场景2:登录状态下, 点击我的帖子

☑️预期结果:  跳转到所有贴子页面

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

测试代码如下: 

    @Order(2)
    @ParameterizedTest
    @CsvSource({"zhangsan, 123456"})
    void myAllActicleListByLogin(String username, String password) throws IOException, InterruptedException {
        driver.get("http://127.0.0.1:11451/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_profile")).click();
        Thread.sleep(1000);
        AutoTestUtil.getScreenshotAs(getClass().getName() + "_" + AutoTestUtil.currentMethodName());

    }

⭐测试个人信息页面

❌未登录测试

场景1: 未登录状态下,点击个人中心

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

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

和前面类似, 但是测试用例不能少, 由于篇幅限制就不多演示了

登录状态显示个人信息测试

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

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

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

测试代码如下: 

测试代码如下: 

    @Order(3)
    @ParameterizedTest
    @CsvSource({"zhangsan, 123456, 头顶尖尖"})
    void updateUserMessageByLogin(String username, String password, String nickName) throws IOException, InterruptedException {
        driver.get("http://127.0.0.1:11451/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());


    }

场景3:登录后点击用户头像下的个人中心,输入新密码并修改,其中两次密码一致,输入原密码也正确,观察是否跳转回登录界面

☑️预期结果: 跳转回登录界面, 强制重新登录

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

测试代码如下: 

    @Order(4)
    @ParameterizedTest
    @CsvSource({"zhangsan, 123456, 1234567, 123456"})
    void updatePasswordByLogin(String username, String password, String newPassword, String repeatPassword) throws IOException, InterruptedException {
        driver.get("http://127.0.0.1:11451/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();
        
        WebElement element = driver.findElement(By.cssSelector("#settings_submit_password"));
        // 滚动到下方
        ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView();", element);
        // 确保滚动成功
        Thread.sleep(1000);

        driver.findElement(By.cssSelector("#settings_input_oldPassword")).sendKeys(password);
        driver.findElement(By.cssSelector("#settings_input_newPassword")).sendKeys(newPassword);
        driver.findElement(By.cssSelector("#settings_input_passwordRepeat")).sendKeys(repeatPassword);
        
        driver.findElement(By.cssSelector("#settings_submit_password")).click();
        
        Thread.sleep(1000);
        AutoTestUtil.getScreenshotAs(getClass().getName() + "_" + AutoTestUtil.currentMethodName());

    }

⭐测试退出

场景1:登录后,点击用户头像下的退出按钮,跳转回登录界面,退出成功

最后释放驱动对象

☑️预期结果: 跳转回登录界面

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

测试代码: 

    @Order(4)
    @ParameterizedTest
    @CsvSource({"zhangsan, 123456"})
    void exit(String username, String password) throws IOException, InterruptedException {
        driver.get("http://127.0.0.1:11451/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_logout")).click();

        Thread.sleep(1000);
        AutoTestUtil.getScreenshotAs(getClass().getName() + "_" + AutoTestUtil.currentMethodName());
        
        // 最后释放驱动对象
        driver.quit();
    }

面试:

【面试】如何实现的,以及有什么亮点?

实现方式: 

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

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

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

亮点:

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

相关文章:

  • 程序化广告行业(28/89):基于用户旅程的广告策略解析
  • 差分 异或
  • 网络编程之客户端聊天(服务器加客户端共三种方式)
  • 智能家居安全革命:代理IP如何守护物联网世界
  • Elasticsearch:使用 ColPali 进行复杂文档搜索 - 第 1 部分 - 8.18
  • 穿越禁区:前端跨域通信的艺术与实践
  • Deployment声明式更新与应用式更新对比
  • Weblogic未授权远程命令执行漏洞复现
  • string(1):
  • 基于pycatia的CATIA装配体STP批量导出技术解析与优化指南
  • 分治-快速排序系列一>快速排序
  • VMWare:解决Linux虚拟机找不到共享文件夹
  • Java单元测试、Junit、断言、单元测试常见注解、单元测试Maven依赖范围、Maven常见问题解决方法
  • ubuntu高并发内核参数调优 - (压测客户端调优)
  • 【面试场景题-Redis中String类型和map类型的区别】
  • 蓝桥杯练习day2:执行操作后的变化量
  • 如何判断 MSF 的 Payload 是 Staged 还是 Stageless(含 Meterpreter 与普通 Shell 对比)
  • MySQL:数据库基础
  • 解决虚拟机网络问题
  • 【论文笔记】VGGT-从2D感知3D:pose估计+稠密重建+点跟踪
  • 国家能源局:鼓励各地探索深远海、沙戈荒等可再生能源制氢场景
  • 洪纬读《制造三文鱼》丨毒素缠身的水生鸡
  • AI世界的年轻人|与总书记交流过的“00后”,渴望紧握时代赋予的接力棒
  • 苏州一直升机坠落致1死4伤,事故调查正展开
  • 5月1日,多位省级党委书记调研旅游市场、假期安全等情况
  • 五一去哪玩?“时代交响”音乐会解锁艺术假期