在线音乐播放器测试

news2024/11/24 2:51:51

在线音乐播放器测试报告

文章目录

  • 1. 项目背景
  • 2. 项目简介
      • 2.1登录页面
      • 2.2 主页面
      • 2.3 收藏页面
      • 2.4 上传音乐页面
      • 2.5 注册页面
  • 3. 需求评测
  • 4. 制定测试计划
  • 5. 设计测试用例
    • 5.1 注册
    • 5.2 登录
    • 5.3 主页面
    • 5.4 收藏页面
    • 5.5 添加歌曲
  • 6. 执行测试用例
    • 6.1 注册
    • 6.2 登录
    • 6.2 主页面
    • 6.3 收藏页面

1. 项目背景

在线音乐播放器是一个简单的个人项目,主要通过项目来学习 spring boot , mybatis,spring AOP 等框架,以及测试流程

2. 项目简介


项目主要功能: 登录,注册,上传音乐,播放音乐,收藏音乐,搜索音乐,批量删除音乐等.

2.1登录页面


业务描述: 管理员用户需要通过登录页面进入在线音乐播放器系统,登录页面是进入系统的唯一页面

需求描述: 管理员需要输入用户名,密码才能登该系统.

行为人: 管理员

UI 界面:
在这里插入图片描述


业务规则:

  1. 用户名长度 1-8 区分大小写但不含特殊字符.
  2. 用户密码长度为 6 - 15 且不含特殊字符 当用户输入完用户名和用户密码点击登录才能进入系统.

2.2 主页面


业务描述: 登录系统后,管理员可以上传音乐,播放音乐,对上传后的音乐点击喜欢加入收藏,可以删除已上传的音乐,还可以通过搜索框对 音乐进行搜索. 除了但个删除单个收藏外,还支持 批量删除音乐和收藏音乐.


需求描述:

上传音乐 : 用户点击添加歌曲按钮后,跳转到上传歌曲页面.

播放音乐 : 用户选中歌曲后,点击播放音乐,网站通过读取服务器本地音乐文件进行播放.

添加喜欢: 用户选中歌曲后,点击喜欢按钮,歌曲被收藏然后跳转到收藏页面.

删除音乐: 用户选中歌曲后,点击删除按钮,歌曲被删除,如果歌曲再用户的收藏列表,歌曲一同被删除.

搜索音乐: 用户可以在搜索框输入歌曲名(可以不完整,支持模糊查询)然后点击查询 ,页面出现需要搜索的歌曲.

批量删除/喜欢: 用户对多首歌曲点击删除或喜欢,歌曲被删除或被添加至喜欢列表.

行为人: 管理员

UI 界面:

在这里插入图片描述

业务规则:

  1. 主页面的按钮点击后能供切换颜色,让用户感觉出按钮被点击.
  2. 用户为在输入框输入歌曲名点击查询将数据库中的歌曲全部查出放到页面
  3. 用户在输入框输入歌曲名应支持模糊查询 比如 输入 白,页面应将 歌曲含 白 的歌曲 都查出来并放到页面上.

2.3 收藏页面


业务描述: 在主页面点击喜欢列表后跳转至收藏页面,在收藏页面可以对收藏的音乐进行播放,删除,批量删除,搜索音乐.

需求描述:

播放音乐 : 用户选中歌曲后,点击播放音乐,网站通过读取服务器本地音乐文件进行播放.

删除音乐: 用户选中歌曲后,点击删除按钮,歌曲被删除,如果歌曲再用户的收藏列表,歌曲一同被删除.

搜索音乐: 用户可以在搜索框输入歌曲名(可以不完整,支持模糊查询)然后点击查询 ,页面出现需要搜索的歌曲.

批量删除: 用户对多首歌曲点击删除,歌曲被删除

行为人: 管理员

UI 界面:

在这里插入图片描述

业务规则:

  1. 主页面的按钮点击后能供切换颜色,让用户感觉出按钮被点击.
  2. 用户为在输入框输入歌曲名,点击查询将当前用户喜欢的音乐全部展示.
  3. 用户在输入框输入歌曲名应支持模糊查询 比如 输入 白,在当前用户收藏的音乐进行搜索,如果没有找到表格置空

2.4 上传音乐页面


业务描述: 用户在主页面点击添加歌曲跳转到上传音乐页面,在上传音乐页面,点击选择文件可以从本地上传 mp3 文件, 输入完歌手名后点即上传按钮完成音乐上传.

需求描述: 用户点击上传文件,从本地选中 mp3 文件后,在输入完歌手名后,点击上传按钮,对文件进行上传.

行为人: 管理员

UI 界面:

在这里插入图片描述

业务规则: 用户上传重复文件后,系统报错,提示文件已上传.

2.5 注册页面

业务描述: 用于注册用户

需求描述 : 用户 进入 注册页面后,输入用户名和密码,再次确认密码后,点击确定完成注册.

行为人: 用户

UI 界面:

在这里插入图片描述


业务规则:

  1. 用户名区分大小写但不含特殊字符
  2. 用户密码长度为 6 - 15且不含特殊字符, 当用户输入完用户名 , 密码和确认密码后才能点击按钮进行注册

3. 需求评测

4. 制定测试计划


目的 : 对在线音乐播放器进行测试.


测试内容:

模块名称测试点优先级说明
用户模块注册功能测试
用户模块登录并发性测试
主模块上传音乐功能测试
主模块播放音乐功能测试
主模块收藏音乐功能测试
主模块搜索音乐功能测试
主模块删除音乐功能测试
主模块批量删除/喜欢音乐功能测试
收藏模块播放音乐功能测试
收藏模块取消收藏功能测试
收藏模块搜索音乐功能测试
收藏模块批量取消收藏功能测试
上传模块上传音乐功能测试
上传模块上传大额文件性能测试


测试环境 :

  1. Windows
  2. 浏览器 Chrome 版本: 125.0.6422.142(正式版本) (64 位)


测试方法: 黑盒测试

测试工具: ApiPost , Selenium , Junit


测试人员 : 牧..

5. 设计测试用例

5.1 注册


用户名 : 用户名长度 1-8 区分大小写但不含特殊字符.

密码: 用户密码长度为 6 - 15, 当用户输入完用户名和用户密码点击登录才能进入系统

划分等价类

输入条件有效等价类无效等价类
用户名不含特殊字符且长度在 1-8 之间空白字符串
用户名含有特殊字符
用户名长度大于 8
密码不含特殊字符
长度在 6 - 15 之间
空白字符串
密码含有特殊字符
密码长度大于8

边界值

用户名密码
用户名长度: 0,1,2,7,8,9密码长度:5,6,7,14,15,16

测试用例

方案用户名密码确认密码注册预期说明
1abc123456123456有效注册成功,跳蛛至登录页面用户名不含特殊字符且大于1小于8
密码和确认密码相同,密码长度大于 6 小于 15
2123456123456无效注册失败,弹出提框用户名不能为空用户名长度小于1
3a123456123456有效注册成功,跳蛛至登录页面用户名不含特殊字符,用户名长度正好达到最低要求
密码和确认密码相同,密码长度大于 6 小于 15
4ab123456123456有效注册成功,跳蛛至登录页面用户名不含特殊字符,
用户名长度 大于 1 小于 8
密码和确认密码相同,密码长度大于 6 小于 15
5abc1234123456123456有效注册成功,跳蛛至登录页面用户名不含特殊字符,
用户名长度 大于 1 小于 8
密码和确认密码相同,密码长度大于 6 小于 15
6abc12345123456123456有效注册成功,跳蛛至登录页面用户名不含特殊字符,
用户名长度 大于 1 小于 8
密码和确认密码相同,密码长度大于 6 小于 15
7abc123456123456123456无效注册失败,弹出提示用户名长度大于8用户名长度大于8
8abc""\123456123456无效注册失败,弹出提示用户名含有特殊字符用户名含有特殊字符 " /
9abc1234512345无效注册失败,弹出提示密码长度小于6密码长度小于6
10abc12345671234567有效注册成功,跳蛛至登录页面用户名不含特殊字符,
用户名长度 大于 1 小于 8
密码和确认密码相同,密码长度大于 6 小于 15
11abc1234567123456712345671234567有效注册成功,跳蛛至登录页面用户名不含特殊字符,
用户名长度 大于 1 小于 8
密码和确认密码相同,密码长度大于 6 小于 15
12abc123456712345671123456712345671有效注册成功,跳蛛至登录页面用户名不含特殊字符,
用户名长度 大于 1 小于 8
密码和确认密码相同,密码长度大于 6 小于 15
13abc12345671234567121234567123456712无效注册失败,弹出提示密码长度大于15密码长度大于15
14abc123456无效注册失败,弹出提示框,密码不能为空密码长度小于6
15abc123456无效注册失败,弹出提示框,提示再次输入密码确认密码为空

5.2 登录

测试用例

方案用户密码登录预期说明
16输入已注册的用户名: abc123456有效登录成功跳转至主页面用户和密码已经保存在数据库
17输入未注册的用户名: abcd123456无效登录失败,弹出提示框提示用户未注册用户未注册 (数据库查询不到)
18输入含有特殊字符的用户名123456无效登录失败,弹出提示框提示用户名含有特殊字符用户名含有特殊字符
19输入已注册的用户名: abc12345无效登录失败,弹出提示框提示密码长度小于6密码长度小于6
20输入已注册的用户名: abc1234567
(错误密码)
无效登录失败,弹出提示框用户或密码错误密码错误
21输入已注册的用户名: abc1234567123456712无效登录失败,弹出提示框密码长度大于15密码长度大于15

5.3 主页面

方案功能操作步骤预期结果
22播放音乐选中哆啦A梦的播放音乐按钮点击音乐播放成功
23收藏歌曲选中需要收藏的歌曲点击喜欢按钮音乐被收藏,然后跳转至收藏页面
24收藏歌曲选中用户已收藏的歌曲点击喜欢按钮弹出提示框歌曲已收藏
25删除音乐选中要删除的歌曲点击删除按钮歌曲删除
26批量删除音乐选中需要删除的歌曲点击删除按钮歌曲删除
27校验登录未登录用户通过 URL 直接进入页面弹出提示框提示用户登录然后页面跳转至登录页面
28查找音乐用户在输入框输入完整歌曲名 如 哆啦A梦页面展示出有关哆啦A梦的歌曲
29查找音乐用户在输入框输入 部分歌曲名称 如 哆页面展示出有关 哆 的歌曲
30跳转功能用户点击添加歌曲或喜欢列表按钮页面跳转至上传文件页面或收藏页面

5.4 收藏页面

方案功能操作步骤预期结果
31播放音乐选中哆啦A梦的播放音乐按钮点击音乐播放成功
32取消收藏选中要取消收藏的歌曲点击删除按钮歌曲删除
33校验登录未登录用户通过 URL 直接进入页面弹出提示框提示用户登录然后页面跳转至登录页面
34查找音乐用户在输入框输入完整歌曲名 如 哆啦A梦页面展示出有关哆啦A梦的歌曲
35查找音乐用户在输入框输入 部分歌曲名称 如 哆页面展示出有关 哆 的歌曲
36跳转功能用户点击回到首页按钮页面跳转至主页面

5.5 添加歌曲

方案功能操作步骤预期结果
37上传歌曲点击选中文件,在本地选中要上出的 mp3 文件
然后输入歌手名点击上传
弹出提示框文件上传成功,页面跳转至主页面

6. 执行测试用例

搭建自动化测试环境: jdk8 + selenium + junit

创建 maven 项目 导入相关依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>csdn_test_demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- selenium 相关依赖-->
        <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>
        <!-- Junit 相关依赖-->
        <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-suite</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>
    </dependencies>

</project>


创建 Constants,创建常量存储 url :

package constants;

public class Constants {
    //    public static final String LOGIN_URL = "https://www.csdn.net/";
    public static final String ALL_REQUEST = "--remote-allow-origins=*";
    public static final String MY_LOGIN_URL = "http://127.0.0.1:8080/login.html";
    public static final String MY_INDEX_URL = "http://127.0.0.1:8080/list.html";
    public static final String MY_REGISTER_URL = "http://127.0.0.1:8080/register.html";
    public static final String MY_LOVEMUSIC_URL = "http://127.0.0.1:8080/loveMusic.html";
    public static final String MY_UPLOADMUSICFILE_URL = "http://127.0.0.1:8080/upload.html";
    public static final String MY_FILE_UPLOAD_PATH = "D:/SSM项目存放/project/j_music/test/";

}

创建 DriverUtils ,更好获取 webDriver

package utils;

import constants.Constants;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;


public class DriverUtils {
    public static WebDriver getWebDriver() {
        ChromeOptions options = new ChromeOptions();
        // 允许浏览器接受来自任何源的远程请求
        options.addArguments(Constants.ALL_REQUEST);
        return new ChromeDriver(options);
    }
}

6.1 注册

页面 符合 UI 设计

在这里插入图片描述


编写自动化测试


在 resources 下创建 registerTest.csv 文件 存放 要测试的数据

在这里插入图片描述


测试数据

abc,123456,123456
,123456,123456
a,123456,123456
ab,123456,123456
abc1234,123456,123456
abc12345,123456,123456
abc123456,123456,123456
abc""\,123456,123456
abce,12345,12345
abcd,1234567,1234567
abcde,12345671234567,12345671234567
abcdef,123456712345671,123456712345671
abcf,1234567123456712,1234567123456712
abc,123456,
abc,,123456

创建 registerTest 类 , 编写自动化测试方法

package register;

import constants.Constants;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
import org.junit.jupiter.params.provider.CsvSource;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import utils.DriverUtils;

public class RegisterTest {

    private WebDriver webDriver;

    @AfterEach
    public void quit() {
        webDriver.quit();
    }

    @ParameterizedTest
    @CsvSource(value = "黑蛋243,123456,123456")
    public void singleRegister(String name, String password, String repetitionPassword) throws InterruptedException {
        WebDriver webDriver = DriverUtils.getWebDriver();
        webDriver.get(Constants.MY_REGISTER_URL);
        webDriver.findElement(By.cssSelector("#user")).sendKeys(name);
        webDriver.findElement(By.cssSelector("#password")).sendKeys(password);
        webDriver.findElement(By.cssSelector("#repetition_password")).sendKeys(repetitionPassword);
        webDriver.findElement(By.cssSelector("#submit")).click();
        WebDriverWait wait = new WebDriverWait(webDriver, 10);
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = webDriver.switchTo().alert();
        alert.accept();
        String currentUrl = webDriver.getCurrentUrl();
        Assertions.assertEquals(currentUrl, Constants.MY_LOGIN_URL);
    }

    @ParameterizedTest
    @CsvFileSource(resources = "/registerTest.csv")
    public void batchRegister(String name, String password, String repetitionPassword) {
        webDriver = DriverUtils.getWebDriver();
        webDriver.get(Constants.MY_REGISTER_URL);
        webDriver.findElement(By.cssSelector("#user")).sendKeys(name == null ? "" : name);
        webDriver.findElement(By.cssSelector("#password")).sendKeys(password == null ? "" : password);
        webDriver.findElement(By.cssSelector("#repetition_password")).sendKeys(repetitionPassword == null ? "" : repetitionPassword);
        webDriver.findElement(By.cssSelector("#submit")).click();
        WebDriverWait wait = new WebDriverWait(webDriver, 10);
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = webDriver.switchTo().alert();
        alert.accept();
        String currentUrl = webDriver.getCurrentUrl();
        Assertions.assertEquals(currentUrl, Constants.MY_LOGIN_URL);
//        webDriver.quit();
    }
}

执行 batchRegister 方法 :

在这里插入图片描述

执行完自动化后,会发现出了密码为空和确认密码为空的两个用例没有过,其他的都通过了就说明:

  1. 系统没有对用户名做非空验证
  2. 系统没有对用户名做长度校验
  3. 系统没有对用户名是否含有特殊字符做校验
  4. 系统没有对密码长度做判断

此时数据库:

在这里插入图片描述

6.2 登录


页面符合 UI 设计

在这里插入图片描述

验证按钮是否可点击:

package login;

import constants.Constants;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import utils.DriverUtils;

import java.util.concurrent.TimeUnit;

public class MyMusicWebsiteLoginTest {

    private WebDriver driver;

    @BeforeEach
    public void getWebDriver() {
        driver = DriverUtils.getWebDriver();
    }

    @AfterEach
    public void quit() {
        driver.quit();
    }

    // 验证登录按钮是否有用
    @Test
    public void checkLongButton() {
        driver.get(Constants.MY_LOGIN_URL);
        // 找到 button 按钮进行点击
        driver.findElement(By.cssSelector("#submit")).click();
        // 校验是否出现弹出框
        WebDriverWait wait = new WebDriverWait(driver, 6);
        // 等待弹出框
        Alert alert = wait.until(ExpectedConditions.alertIsPresent());
        Assertions.assertNotNull(alert);
        alert.accept();
    }
}


测试通过

在这里插入图片描述


测试登录功能:

  1. 登录成功: 判断依据用户输入完账户密码后点击确认按钮,成功进入到主页面 (判断当前 页面 url 是否为主页面 url 即可)
  2. 登录失败 : 判断是点击完弹窗是否还是在 登录页面即可

编写自动化测试脚本:


    @ParameterizedTest
    @CsvSource(value = "abc,123456")
    public void checkLoginSuccess(String name, String password) {
        driver.get(Constants.MY_LOGIN_URL);
        driver.findElement(By.cssSelector("#user")).sendKeys(name == null ? "" : name);
        driver.findElement(By.cssSelector("#password")).sendKeys(password == null ? "" : password);
        driver.findElement(By.cssSelector("#submit")).click();
        // 此时出现 alter
        WebDriverWait wait = new WebDriverWait(driver, 5);
        // 显示等待出现弹出框
        Alert alert = wait.until(ExpectedConditions.alertIsPresent());
        // 判断 alert --> 到这里就可以判断出否登录成功了
        Assertions.assertEquals("登录成功!!!", alert.getText());
        alert.accept();
        // 在这里还可以通过判断 url
        String currentUrl = driver.getCurrentUrl();
        Assertions.assertEquals(Constants.MY_INDEX_URL, currentUrl);
    }


测试结果:

在这里插入图片描述

构造测试数据(登录失败用例): 在 resources 创建 logintest.csv 在文件中输入一下数据.

abc,123456
abcd,123456
abc/"",123456
abc,12345
abc,1234567
abc,1234567123456712

编写自动化脚本

  @ParameterizedTest
    @CsvFileSource(resources = "/logintest.csv")
    public void checkLoginFail(String name, String password) {
        driver.get(Constants.MY_LOGIN_URL);
        driver.findElement(By.cssSelector("#user")).sendKeys(name == null ? "" : name);
        driver.findElement(By.cssSelector("#password")).sendKeys(password == null ? "" : password);
        driver.findElement(By.cssSelector("#submit")).click();
        // 显示等待 弹出框
        WebDriverWait wait = new WebDriverWait(driver, 5);
        Alert alert = wait.until(ExpectedConditions.alertIsPresent());

        if (name == null || name.equals("") || Objects.equals(password, "")) {
            Assertions.assertEquals("用户名或密码为空 !!!", alert.getText());
        } else {
            Assertions.assertEquals("用户名或密码错误 !!!!", alert.getText());
        }
        alert.accept();
        // 判断 url
        Assertions.assertEquals(Constants.MY_LOGIN_URL, driver.getCurrentUrl());
    }

在这里插入图片描述

这里 abc , 123456 是 正确的账号和密码 所以被拦住了

看完上面运行会发现问题:

  1. 未对用户名做长度校验
  2. 未对密码做长度校验
  3. 未校验用户名是否含有特殊字符

6.2 主页面


页面符合 UI 设计

在这里插入图片描述


执行测试用例:

方案功能操作步骤预期结果
22播放音乐选中哆啦A梦的播放音乐按钮点击音乐播放成功
23收藏歌曲选中需要收藏的歌曲点击喜欢按钮音乐被收藏,然后跳转至收藏页面
24收藏歌曲选中用户已收藏的歌曲点击喜欢按钮弹出提示框歌曲已收藏
25删除音乐选中要删除的歌曲点击删除按钮歌曲删除
26批量删除音乐选中需要删除的歌曲点击删除按钮歌曲删除
27校验登录未登录用户通过 URL 直接进入页面弹出提示框提示用户登录然后页面跳转至登录页面
28查找音乐用户在输入框输入完整歌曲名 如 哆啦A梦页面展示出有关哆啦A梦的歌曲
29查找音乐用户在输入框输入 部分歌曲名称 如 哆页面展示出有关 哆 的歌曲
30跳转功能用户点击添加歌曲或喜欢列表按钮页面跳转至上传文件页面或收藏页面
31上传音乐点击选中文件,在本地选中要上出的 mp3 文件
然后输入歌手名点击上传
弹出提示框文件上传成功,页面跳转至主页面

编写自动化脚本:

package login;

import constants.Constants;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
import org.junit.jupiter.params.provider.CsvSource;
import org.openqa.selenium.*;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import utils.DriverUtils;

import java.time.Duration;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeUnit;


@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MainPageTest {

    private static WebDriver webDriver;

    // 在所有方法执行之前执行一下这个获取 driver 的方法
    @BeforeAll
    static void getWebDriver() {
        webDriver = DriverUtils.getWebDriver();
    }

    // 在所有方法执行之后 对 driver 进行关闭
    @AfterAll
    static void quit() {
        webDriver.quit();
    }

    @Order(1)
    @Test
    // 未登录直接通过 url 进入 主页面
    public void noLoginEnterIHomePage() {
        webDriver.get(Constants.MY_INDEX_URL);
        // 此时会直接回到登录页面
        String currentUrl = webDriver.getCurrentUrl();
        Assertions.assertEquals(Constants.MY_LOGIN_URL, currentUrl);
    }


    //  登录
    @Order(2)
    @ParameterizedTest
    @CsvSource(value = "abc,123456")
    public void login(String name, String password) {
//        webDriver.get(Constants.MY_LOGIN_URL);
        webDriver.findElement(By.cssSelector("#user")).sendKeys(name == null ? "" : name);
        webDriver.findElement(By.cssSelector("#password")).sendKeys(password == null ? "" : password);
        webDriver.findElement(By.cssSelector("#submit")).click();
        // 此时出现 alter
        WebDriverWait wait = new WebDriverWait(webDriver, 5);
        // 显示等待出现弹出框
        Alert alert = wait.until(ExpectedConditions.alertIsPresent());
        // 判断 alert --> 到这里就可以判断出否登录成功了
        Assertions.assertEquals("登录成功!!!", alert.getText());
        alert.accept();
    }


    @Order(3)
    @Test
    public void playMusic() {
        webDriver.findElement(By.cssSelector("#info > tr:nth-child(1) > td:nth-child(4) > button")).click();

        // 此时判断歌曲是否被播放,只要判断播放器,上显示歌曲的名字是否不为 空即可
        String musicName = webDriver.findElement(By.cssSelector("#body > div:nth-child(2) > div > div > div.topbar > div.topbar-program > div.topbar-program-title")).getText();
        Assertions.assertNotEquals("", musicName);
    }

    @Order(4)
    @Test
    public void loveMusic() {
        webDriver.findElement(By.cssSelector("#info > tr:nth-child(1) > td:nth-child(5) > button:nth-child(2)")).click();
        // 弹出提示框
        WebDriverWait wait = new WebDriverWait(webDriver, 5);
        Alert alert = wait.until(ExpectedConditions.alertIsPresent());
        String loveMusicDesc = alert.getText();
        alert.accept();
        if (loveMusicDesc.equals("歌曲已经收藏,请重新选择")) {
            return;
        }
        Assertions.assertEquals("添加成功", loveMusicDesc);
//        webDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
        // 此时跳转至喜欢页面
        String currentUrl = webDriver.getCurrentUrl();
        Assertions.assertEquals(Constants.MY_LOVEMUSIC_URL, currentUrl);

        // 此时将喜欢音乐添加成功,将页面退回至主页面 通过窗口操作
        webDriver.navigate().back();
    }

    // 添加重复的音乐
    @Order(5)
    @Test
    public void loveRepetitionMusic() {
        webDriver.findElement(By.cssSelector("#info > tr:nth-child(1) > td:nth-child(5) > button:nth-child(2)")).click();
        // 弹出提示框
        WebDriverWait wait = new WebDriverWait(webDriver, 5);
        Alert alert = wait.until(ExpectedConditions.alertIsPresent());
        String loveMusicDesc = alert.getText();
        Assertions.assertEquals("歌曲已经收藏,请重新选择", loveMusicDesc);
        alert.accept();
//        webDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
        // 此时跳转至喜欢页面
        String currentUrl = webDriver.getCurrentUrl();
        Assertions.assertEquals(Constants.MY_INDEX_URL, currentUrl);

        // 此时将喜欢音乐添加成功,将页面退回至主页面 通过窗口操作
//        webDriver.navigate().back();
    }


    // 上传音乐
    @Order(6)
    @ParameterizedTest
    @CsvFileSource(resources = "/musicName.csv")
    public void uploadMusicFile(String musicName, String singerName) {
        webDriver.findElement(By.cssSelector("#body > div.container > div:nth-child(3) > a:nth-child(2)")).click();
        // 此时页面进行跳转
        // 上传文件 1.找到上传文件按钮,然后通过 sendKeys 输入要上传文件的路径即可
        webDriver.findElement(By.cssSelector("body > form > input[type=file]:nth-child(1)")).sendKeys(Constants.MY_FILE_UPLOAD_PATH + musicName);

        // 输入歌手名 --> 避免上传文件重复 (后端主要同故宫 判断歌曲名和歌手名) 这里就对 歌手名加一个随机数
        singerName = singerName + DriverUtils.getRandomStr(Constants.LEN_5);
        // 输入歌手名
        webDriver.findElement(By.cssSelector("body > form > label > input[type=text]")).sendKeys(singerName);

        // 点击上传按钮
        webDriver.findElement(By.cssSelector("body > form > input[type=submit]:nth-child(3)")).click();

        // 此时校验是否长传成功,判断当前页面的 url 是否为主页面即可
        String currentUrl = webDriver.getCurrentUrl();
//        System.out.println("------------------当前页面Url: " + currentUrl + "----------------");
        Assertions.assertEquals(Constants.MY_INDEX_URL, currentUrl);
    }


    @Order(7)
    @ParameterizedTest
    @CsvFileSource(resources = "/findMusicKeys.csv")
    public void findMusicByKey(String key) {
        // 找到查找框
        webDriver.findElement(By.cssSelector("#exampleInputName2")).sendKeys(key);
        // 点击查询按钮
        webDriver.findElement(By.cssSelector("#submit1")).click();

        // 拿到整个 表格
        WebElement tableElement = webDriver.findElement(By.cssSelector("#info"));

        // 获取 表格中的每一行的数据
        List<WebElement> trElements = tableElement.findElements(By.tagName("tr"));
        // 这里的结构是
        // <tr>xxxxx<tr/>
        // <tr><tr/>
        for (WebElement nowTrLabel : trElements) {
            // 获取到当前 tr 标签
            // 获取标签中的 th 元素
            List<WebElement> thElements = nowTrLabel.findElements(By.tagName("td"));
            if (thElements.isEmpty()) {
                continue;
            }
            // 此时不为空 -- 校验 musicName
            WebElement thElement = thElements.get(0);
            // 判断 音乐名是否含有 key 比如 哆啦a梦 是否含有 哆
            Assertions.assertTrue(thElement.getText().contains(key));
        }

        // 此时查找完成后,再次找到输入框 输入 "" , 再次点击查询按钮 查询到所有的音乐
        // 找到查找框
//        webDriver.findElement(By.cssSelector("#exampleInputName2")).sendKeys("");
        // 点击查询按钮
        webDriver.findElement(By.cssSelector("#submit1")).click();
    }

    // 删除音乐 --> 删除单个音乐
    @Order(8)
    @Test
    public void deleteMusic() {
        // 获取 表格
        WebElement tableElement = webDriver.findElement(By.cssSelector("#info"));

        // 获取 表格中的每一行的数据
        List<WebElement> trElements = tableElement.findElements(By.tagName("tr"));

        int totalSize = trElements.size();

        // 删除最后一个
        WebElement finallyTrElement = trElements.get(trElements.size() - 2);

        // 拿到勾选框
        List<WebElement> thElements = finallyTrElement.findElements(By.tagName("th"));
        List<WebElement> tdElements = finallyTrElement.findElements(By.tagName("td"));

        // 找到 input 框进行点击
        thElements.get(0).findElement(By.tagName("input")).click();
        // 找到删除按钮
        List<WebElement> buttons = tdElements.get(3).findElements(By.tagName("button"));
        WebElement deleteButton = buttons.get(0);
        deleteButton.click();
        // 此时删除音乐弹出对话框
        WebDriverWait wait = new WebDriverWait(webDriver, 5);
        Alert alert = wait.until(ExpectedConditions.alertIsPresent());
        alert.accept();
        // 判断是否删除 重新获取 table 判断个数即可

        // 获取 表格
        tableElement = webDriver.findElement(By.cssSelector("#info"));

        // 获取 表格中的每一行的数据
        trElements = tableElement.findElements(By.tagName("tr"));

        Assertions.assertEquals(totalSize - 2, trElements.size());
    }


    // 批量删除音乐
    @Order(9)
    @ParameterizedTest
    @CsvSource(value = "3")
    public void batchDeleteMusic(Integer count) {

        // 获取整个表格
        WebElement tableElement = webDriver.findElement(By.cssSelector("#info"));

        // 获取 tr 标签
        List<WebElement> trElements = tableElement.findElements(By.tagName("tr"));

        int totalSize = trElements.size();

        // 循环勾选
        for (int i = 0; i < count * 2; i += 2) {
            // 拿到勾选框
            WebElement nowTrElement = trElements.get(i);
            // 获取 th 标签里面的 input
            WebElement thElement = nowTrElement.findElement(By.tagName("th"));
            // 找到 input 框 进行勾选
            thElement.findElement(By.tagName("input")).click();
        }

        // 此时该删除的都勾选了 点击 批量删除按钮
        webDriver.findElement(By.cssSelector("#delete")).click();
        WebDriverWait wait = new WebDriverWait(webDriver, 5);
        Alert alert = wait.until(ExpectedConditions.alertIsPresent());
        alert.accept();

        // 校验是否删除成功 --> 判断当前表格 tr 个数即可
        tableElement = webDriver.findElement(By.cssSelector("#info"));
        trElements = tableElement.findElements(By.tagName("tr"));
        
        Assertions.assertEquals(totalSize - count * 2, trElements.size());
    }
}


执行脚本:

在这里插入图片描述

校验登录不符合预期结果,用户在未登录的情况下,进入 主页面并没有弹出提示框而是直接回到登录页面.

6.3 收藏页面

页面符合 UI 设计

在这里插入图片描述

方案功能操作步骤预期结果
32播放音乐选中哆啦A梦的播放音乐按钮点击音乐播放成功
33取消收藏选中要取消收藏的歌曲点击删除按钮歌曲删除
34校验登录未登录用户通过 URL 直接进入页面弹出提示框提示用户登录然后页面跳转至登录页面
35查找音乐用户在输入框输入完整歌曲名 如 哆啦A梦页面展示出有关哆啦A梦的歌曲
36查找音乐用户在输入框输入 部分歌曲名称 如 哆页面展示出有关 哆 的歌曲
36跳转功能用户点击回到首页按钮页面跳转至主页面
37校验登录未登录用户通过 URL 直接进入页面弹出提示框提示用户登录然后页面跳转至登录页面

编写自动化脚本:

package login;

import constants.Constants;
import org.checkerframework.checker.units.qual.A;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
import org.junit.jupiter.params.provider.CsvSource;
import org.openqa.selenium.*;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import utils.DriverUtils;

import java.awt.*;
import java.lang.annotation.Documented;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Random;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class LovePageTest {
    private static WebDriver webDriver;

    // 在所有方法执行之前执行一下这个获取 driver 的方法
    @BeforeAll
    static void getWebDriver() {
        webDriver = DriverUtils.getWebDriver();
    }

    // 在所有方法执行之后 对 driver 进行关闭
    @AfterAll
    static void quit() {
        webDriver.quit();
    }

    @Order(1)
    @Test
    // 未登录直接通过 url 进入 收藏页面
    public void noLoginEnterIHomePage() {
        webDriver.get(Constants.MY_LOVEMUSIC_URL);
        // 此时会直接回到登录页面
        String currentUrl = webDriver.getCurrentUrl();

        Assertions.assertEquals(Constants.MY_LOGIN_URL, currentUrl);
    }

    //  登录
    @Order(2)
    @ParameterizedTest
    @CsvSource(value = "abc,123456")
    public void login(String name, String password) {

//        webDriver.get(Constants.MY_LOGIN_URL);
        webDriver.findElement(By.cssSelector("#user")).sendKeys(name == null ? "" : name);
        webDriver.findElement(By.cssSelector("#password")).sendKeys(password == null ? "" : password);
        webDriver.findElement(By.cssSelector("#submit")).click();
        // 此时出现 alter
        WebDriverWait wait = new WebDriverWait(webDriver, 5);
        // 显示等待出现弹出框
        Alert alert = wait.until(ExpectedConditions.alertIsPresent());
        // 判断 alert --> 到这里就可以判断出否登录成功了
        Assertions.assertEquals("登录成功!!!", alert.getText());
        alert.accept();
    }

    // 防止收藏页面没有歌曲,先对歌曲进行收藏
    @Order(3)
    @Test
    public void loveMusic() {
        // 拿到表格
        WebElement tableElement = webDriver.findElement(By.cssSelector("#info"));
        List<WebElement> trElements = tableElement.findElements(By.tagName("tr"));
//        int randomUnevenNumber = DriverUtils.getRandomEvenNumber(trElements.size());
        // 拿到勾选框
        List<WebElement> thElements = null;
        List<WebElement> tdElements = null;
        while (true) {
            int size = new Random().nextInt(trElements.size());
            thElements = trElements.get(size).findElements(By.tagName("th"));
            tdElements = trElements.get(size).findElements(By.tagName("td"));
            if (thElements.size() == 1) {
                break;
            }
        }
        thElements.get(0).click();
        // 拿到喜欢按钮

        WebElement treeTdElement = tdElements.get(3);
        List<WebElement> buttons = treeTdElement.findElements(By.tagName("button"));
        WebElement loveButton = buttons.get(1);
        // 使用JavaScript滚动到元素位置 --> 要不然找会找不到按钮点击会报错
//        ((JavascriptExecutor) webDriver).executeScript("arguments[0].scrollIntoView(true);", loveButton);
//        loveButton.click();

        // 上面移动还是有点问题 直接 调用 javascript 进行点击即可
        // 使用JavaScript点击元素
        ((JavascriptExecutor) webDriver).executeScript("arguments[0].click();", loveButton);

        // 点击弹窗
        WebDriverWait wait = new WebDriverWait(webDriver, 5);
        Alert alert = wait.until(ExpectedConditions.alertIsPresent());

        if (!alert.getText().equals("添加成功")) {
            return;
        }
        alert.accept();
        // 校验 url
        String currentUrl = webDriver.getCurrentUrl();
        Assertions.assertEquals(Constants.MY_LOVEMUSIC_URL, currentUrl);

        // 退回 主页面
        webDriver.navigate().back();
    }

    // 点击添加歌曲按钮进入收藏页面
    @Order(4)
    @Test
    public void clickLoveButton() {
        // 找到按钮进行点击
        webDriver.findElement(By.cssSelector("#body > div.container > div:nth-child(3) > a:nth-child(1)")).click();
        String currentUrl = webDriver.getCurrentUrl();
        Assertions.assertEquals(Constants.MY_LOVEMUSIC_URL, currentUrl);
    }

    // 播放音乐
    @Order(5)
    @Test
    public void playMusic() {
        webDriver.findElement(By.cssSelector("#info > tr:nth-child(1) > td:nth-child(3) > button")).click();
        WebDriverWait wait = new WebDriverWait(webDriver, 5);
        wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("body > div:nth-child(2) > div > div > div.topbar > div.topbar-program > div.topbar-program-title")));
        // 此时判断歌曲是否被播放,只要判断播放器,上显示歌曲的名字是否不为 空即可
        String musicName = webDriver.findElement(By.cssSelector("body > div:nth-child(2) > div > div > div.topbar > div.topbar-program > div.topbar-program-title")).getText();
//        System.out.println(musicName);
        Assertions.assertNotEquals("", musicName);
    }

    // 查找收藏音乐

    @Order(6)
    @ParameterizedTest
    @CsvFileSource(resources = "/findMusicKeys.csv")
    public void findLoveMusicByKey(String key) {
        // 找到查找框
        webDriver.findElement(By.cssSelector("#exampleInputName2")).sendKeys(key);
        // 点击查询按钮
        webDriver.findElement(By.cssSelector("#submit1")).click();
        // 拿到整个 表格
        WebElement tableElement = webDriver.findElement(By.cssSelector("#info"));
        // 获取 表格中的每一行的数据
        List<WebElement> trElements = tableElement.findElements(By.tagName("tr"));
        // 这里的结构是
        // <tr>xxxxx<tr/>
        // <tr><tr/>
        for (WebElement nowTrLabel : trElements) {
            // 获取到当前 tr 标签
            // 获取标签中的 th 元素
            List<WebElement> thElements = nowTrLabel.findElements(By.tagName("td"));
            if (thElements.isEmpty()) {
                continue;
            }
            // 此时不为空 -- 校验 musicName
            WebElement thElement = thElements.get(0);
            // 判断 音乐名是否含有 key 比如 哆啦a梦 是否含有 哆
            Assertions.assertTrue(thElement.getText().contains(key));
        }
        webDriver.findElement(By.cssSelector("#submit1")).click();

    }

    // 移除收藏音乐
    @Order(7)
    @Test
    public void deleteLoveMusic() {
        webDriver.findElement(By.cssSelector("#info > tr:nth-child(1) > td:nth-child(4) > button")).click();
        WebDriverWait wait = new WebDriverWait(webDriver, 5);
        Alert alert = wait.until(ExpectedConditions.alertIsPresent());
        alert.accept();
        String currentUrl = webDriver.getCurrentUrl();
        Assertions.assertEquals(Constants.MY_INDEX_URL, currentUrl);
    }

}

运行结果:

在这里插入图片描述

到此该项目基础功能都测试完成, 简述当前系统存在的 bug

注册:

  1. 未对用户名做长度校验

  2. 未对用户名做特殊字符判断 (用户名包含特殊字符)

  3. 未对用户输入框做判空校验 (用户名可以未空 ,然后进行注册)

  4. 未对用户密码做长度校验

登录:

  1. 未对用户名做长度校验
  2. 未对用户名做特殊字符判断 (用户名包含特殊字符)
  3. 未对用户密码做长度校验

主页面:

  1. 用户在未登录情况下 ,通过 url 进入主页面后,直接跳转至登录页面 (未弹出提示框让用户登录)

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

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

相关文章

凭什么现货白银这么受欢迎?现货白银是什么交易模式

近期现货白银投资颇为火热&#xff0c;很多朋友入场开户成为了正式的现货白银投资者。不过也有一些相对谨慎的投资者&#xff0c;他们想弄明白现货白银交易是什么&#xff0c;运行机制是怎么样的之后再入场&#xff0c;这也是合理的。而他们提出的一个问题就是&#xff0c;现货…

AI “黏土画风”轻松拿捏,手把手带你云端部署 ComfyUI

作者&#xff1a;鸥弋、筱姜 AI 绘画领域&#xff0c;Stable Diffusion WebUI、Midjourney 、DALL-E 都聚拢了一大批的应用开发者和艺术创作者。ComfyUI 出现时间略晚&#xff0c;但是它让创作者通过工作流的方式&#xff0c;实现自动化水平更高的 AI 生图流程&#xff0c;一面…

【Qt 学习笔记】Qt窗口 | 标准对话框 | 文件对话框QFileDialog

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt窗口 | 标准对话框 | 文件对话框QFileDialog 文章编号&#xff1a;Q…

github ssh key的SHA256是什么

github ssh key的SHA256是什么 怎么知道github上自己的公钥指纹和本地的公钥是否一致&#xff1f; 计算方法如下&#xff1a; cat .ssh/id_rsa.pub |awk { print $2 } | # Only the actual key data without prefix or commentsbase64 -d | # decode as base64s…

SqlSugar有实体CURD应用-C#

本文所述开发环境&#xff1a;.C#、NET8、Visual Studio2022 SqlSugar有实体查询数据表 首先根据《SqlSugar使用DbFirst对象根据数据库表结构创建实体类-C#》中的描述的表结构创建所有表的实体类如下&#xff1a; 表名创建的实体类名tb_studentStudenttb_teacherTeachertb_c…

在Lua解释器中注册自定义函数库

本文目录 1、引言2、注册原理3、实例4、程序验证 文章对应视频教程&#xff1a; 暂无&#xff0c;可以关注我的B站账号等待更新。 点击图片或链接访问我的B站主页~~~ 1、引言 在现代软件开发中&#xff0c;Lua因其轻量级、高效和可嵌入性而被广泛使用。作为一种灵活的脚本语言…

(五)React受控表单、获取DOM

1. React受控表单 概念&#xff1a;使用React组件的状态&#xff08;useState&#xff09;控制表单的状态 准备一个React状态值 const [value, setValue] useState()通过value属性绑定状态&#xff0c;通过onChange属性绑定状态同步的函数 <input type"text"…

STL入门指南:从容器到算法的完美结合

目录 ​编辑 一、什么是STL 二、STL的版本 三、STL的六大组件 1. 容器&#xff08;Containers&#xff09;&#xff1a; 2. 算法&#xff08;Algorithms&#xff09;&#xff1a; 3. 迭代器&#xff08;Iterators&#xff09;&#xff1a; 4. 仿函数&#xff08;Functo…

群晖NAS安装配置Joplin Server用来存储同步Joplin笔记内容

一、Joplin Server简介 1.1、Joplin Server介绍 Joplin支持多种方式进行同步用户的笔记数据&#xff08;如&#xff1a;Joplin自己提供的收费的云服务Joplin Cloud&#xff0c;还有第三方的云盘如Dropbox、OneDrive&#xff0c;还有自建的云盘Nextcloud、或者通过WebDAV协议来…

我的高考往事

高考对于每一个参加过的人来说&#xff0c;都是一段非常难忘的回忆。 我参加高考&#xff0c;是在2001年。虽然迄今已经过去了23年&#xff0c;但很多细节仍然记忆犹新。 今天这篇文章&#xff0c;我就和大家分享一下&#xff0c;我的高考往事。 █ 青少年时代 我的老家是在江西…

波拉西亚战记加速器 台服波拉西亚战记免费加速器

波拉西亚战记是一款新上线的MMORPG游戏&#xff0c;游戏内我们有多个角色职业可以选择&#xff0c;可以体验不同的战斗流派玩法&#xff0c;开放式的地图设计&#xff0c;玩家可以自由的进行探索冒险&#xff0c;寻找各种物资。各种随机事件可以触发&#xff0c;让玩家的冒险过…

OZON芒果店长erp,OZON平台支持什么ERP

在跨境电商的浪潮中&#xff0c;OZON平台凭借其独特的优势&#xff0c;吸引了众多卖家入驻。然而&#xff0c;随着业务规模的不断扩大&#xff0c;如何高效管理商品、处理订单、优化仓储物流等成为了卖家们亟需解决的问题。此时&#xff0c;一款强大的ERP&#xff08;企业资源规…

第19篇 Intel FPGA Monitor Program的使用<二>

Q&#xff1a;Intel FPGA Monitor Program里集成的Computer System是什么架构的呢&#xff1f; A&#xff1a;我们以DE2-115的DE2-115_Computer System为例介绍&#xff0c;简单说DE2-115_Computer System就是一个Qsys系统&#xff0c;该系统包含Nios II处理器以及DE2-115开发…

不停“整活”的零食很忙,怎么就跨入万店时代了?

6月12日&#xff0c;合并后的零食很忙、赵一鸣零食宣布&#xff0c;全国门店总数已突破10000家。同时&#xff0c;集团名称也变更为鸣鸣很忙集团。根据第三方机构弗若斯特沙利文认证&#xff0c;鸣鸣很忙集团全国门店数位居零食连锁行业第一。 在此之前&#xff0c;尽管零食很…

8个宝藏级物联网平台推荐

随着物联网技术的飞速发展&#xff0c;越来越多的企业开始寻求高效、可靠的物联网平台来实现设备连接、数据收集和智能分析。以下是8个宝藏级的物联网平台推荐&#xff0c;它们以其独特的功能和优势&#xff0c;帮助企业加速数字化转型。 1. ThingsKit物联网平台 ThingsKit 是…

windows 11中如何设置默认为英文输入法

由于工作需要&#xff0c;我一直在windows7下使用VB6&#xff0c;以前尝试着使用新的系统&#xff0c;但都无法正常安装vb&#xff0c;最近几天由于系统一次作死操作&#xff0c;逼着我安装了win11&#xff0c;并且在其上正常安装了vb6&#xff0c;本想着十分高兴&#xff0c;终…

垂直业务系统权限设计

遵循 RBAC 的原则&#xff0c;以更贴近日常工作的业务处理流程&#xff0c;设计一套与总公司分公司相吻合的组织、部门、岗位结构&#xff0c;配套可以继承的权限组和特定的岗位权限&#xff0c;实现系统授权的操作简化和权限的集成应用简化。 RBAC&#xff08;Role-Based Acce…

C++中的结构体——结构体指针

作用&#xff1a;通过指针访问结构体中的成员 利用操作符 -> 可以通过结构体指针访问结构体属性 示例 运行结果

新研究使VQE算法成功扩展到12个量子比特,误差抑制在两个数量级

量子计算化学已成为量子计算的一个潜在应用领域。 混合量子-经典计算方法&#xff0c;如变分量子本征求解器&#xff0c;已被设计为解决量子化学问题有希望的解决方案。但该计算方法面对着因理论复杂性和实验不完善所带来的挑战&#xff0c;在实现可靠和准确结果方面被限制。因…

史上最全盘点:一文告诉你什么是erp?erp系统厂商分别有哪些?

✅ 什么是ERP&#xff1f; ERP是Enterprise Resource Planning&#xff08;企业资源计划&#xff09;的简称&#xff0c;ERP是针对物资资源管理&#xff08;物流&#xff09;、人力资源管理&#xff08;人流&#xff09;、财务资源管理&#xff08;资金流&#xff09;、信息资…