.net core UI测试
背景
从后端测试的角度来看前端测试;
后端测试
黑盒测试:自动化接口测试,不关心内部实现,只关心入参出参,出于用户角度来测试完整功能;(自动化接口测试)
白盒测试:代码层单元测试,关心内部实现,聚焦核心方法,出于开发角度来测试部分核心功能;(上一篇单元测试)
前端测试
黑盒测试:(本篇实现)
白盒测试:(未实现)
从上面角度来看:
1、如果我们要实现前端黑盒测试,需要从用户角度关心页面功能是否达到目的即可,想办法模拟页面操作,并检验页面元素来校验是否达到期望;
2、如果我们要实现前端白盒测试,那么我们要想办法模拟渲染页面,并在内部测试调用JS方法,通过JS对象或者渲染出的页面元素来判断是否达到期望;
市面上前端测试的流行方案:
黑盒测试:Playwright、Selenium,支持多重前端或后端语言,重点是支持C#;
白盒测试:Jest、Mocha、Jasmine等等,以支持前端语言为主,有些测试框架还需借助第三方库才能完整测试(比如要借助第三方的Mock库、断言库等等),以Vue、Angular、React三大语言对上述各个框架的支持度也不太一样,而且.Net开发对前端代码的规范度和熟悉度没有专业前端开发那么高;
结论:目前以前端黑盒测试来实现前端测试比较主,当然后期随着要求越来越高再来考虑前端白盒测试;
前端黑盒测试的目的
业务目的:自动模拟用户在页面上操作,保证固定的一套页面元素操作下,页面功能达到预期;
代码目的:自动模拟用户在页面上操作,保证JS不会报错;
前端测试选型
Playwright VS Selenium
尝试过Playwright后,比较两者:
- 两者的功能是一致的,Playwright能实现的Selenium也能实现,但是Selenium是建立在依赖各种社区支持勉强实现,语法上不够直观,导致写起来相对Playwright费劲;
- Selenium文档没有完整例子,不好参照;
- Selenium定位不支持模糊查找,定位起来对非专业前端开发来说不轻松;
- Selenium很多东西原生不支持,需借助第三方,导致不知道到底哪些要去找第三方的东西,比如发起Http请求,拦截请求等;
- Selenium弹性等待实现起来不轻松,写法不够简单;
- Selenium录制工具,需下载三方IDE: https://www.selenium.dev/selenium-ide/,Playwright录制起来相对简单,无需三方IDE;
正文
Playwright官方文档:https://playwright.dev/dotnet/docs/intro
步骤一:环境安装
1、安装最新版PowerShell
ps:本地PowerShell一般都是老版本,运行命令会有问题
2、build单元测试项目
3、用PowerShell执行pwsh bin/Debug/net6.0/playwright.ps1 install
ps:注意本地的net版本,可以进项目的bin/Debug/文件夹确认;
4、可能需要重新打开IDE,就可以按步骤三来调试和运行已有测试了
步骤二:UI测试编写
ps:playwright支持Nunit、MsUnit,不支持Xunit(可能是前端对并发的限制),这里选用的是Nunit
1、初始化
继承基类BaseService并在setup中调用Init方法即可;(代码就不共享了,内容如下)
Init方法主要做的内容:
- 初始化浏览器、页面,设置全局超时时间,调试页面大小,运行时是否弹出浏览器
- 登录指定用户并获取token,初始化cookie,前端初始化权限
- 公共对象page(页面实例)、configuration(配置读取)、context(上下本)、browser(浏览器实例)

2、元素定位
- 根据文案查找对象 GetByRole
page.GetByRole(AriaRole.Button, new() { Name = "提交报价单" })
根据按钮名称找到button,模糊搜索且name是button内部元素内的属性都能搜到;
page.GetByRole(AriaRole.Textbox, new() { Name = "输入型号 数量,每行一个" })
根据placeholder找到input;
page.GetByText("提示:报价单转单成功")
根据文案搜索对象,不在乎它是什么对象,只是为了验证它是否已渲染或已显示;
- CSS筛选器 Locator
page.locator("#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input")
- XPath Locator
page.locator('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input')
- Locator + Nth
var orderDivs = page.Locator(".quotation-info");
var testDivs = orderDivs.Nth(1).Locator("div");
var spanDivs = testDivs .Nth(5).Locator("span");
var spanDiv = spanDivs .Nth(2);
- 录制中鼠标定位,生成代码
详见下面介绍的CodeGen:自动录制生成代码
3、元素操作
- Click
- Fill (input输入值)
- Clear
- Focus
- Press("Enter")
- SelectOption
- SetChecked
- Tap
- WaitFor (Attached、Visible、Hidden)
4、操作等待&超时处理
一般在写UI测试时,都需要等待上一个动作结束后才能进行下一个动作,中间的等待动作有两种选择:
- 等待固定时间:await Task.Delay(2000);
- 等待某个UI转变,如等待操作成功的弹窗出现
await submitBtn.WaitForAsync(new LocatorWaitForOptions { State = WaitForSelectorState.Visible, Timeout = 20000 });
前者很明显效率会很低,特别是连续操作的情况下,应少量使用;
后者应设置超时时间,否则会一直等待下去,当然有全局超时设置的情况下,有特殊要求的时候可以设置比全局超时时间更短或更长的时间;所有的元素操作都能超时等参数;
示例:
await submitBtn.ClickAsync();
未设置任何参数时,全局超时起效(10秒)
await matchBtn.ClickAsync(new LocatorClickOptions
{
Delay = 1000
});
按钮事件里,设置了等待1秒后再点击,全局超时起效(10秒)
await submitBtn.WaitForAsync(new LocatorWaitForOptions { State = WaitForSelectorState.Visible, Timeout = 20000 });
等待该元素显示在界面上,超时时间(20秒)
5、接口拦截
await page.RouteAsync("https://ows.yesmro.cn/cs/api/QuotationV2/SubmitQuotation*", async route =>
{
var response = await route.FetchAsync();
var responseTxt = await response.TextAsync();
var responseObj = JsonConvert.DeserializeObject<MstApiResult<QuotationV2Dto>>(responseTxt);
InquiryId = responseObj.Body.Id;
Console.WriteLine(InquiryId);
await route.FulfillAsync(new RouteFulfillOptions
{
Response = response
});
});
拦截Url*,获取返回结果,反序列为对象,处理该对象,接口实现;
6、一般的代码流程
- 初始化页面
- 接口拦截(可选)
- 添加权限脚本(防报错)
-
进入页面
等待页面加载完成
元素定位,并操作元素(比如输入值,点击按钮等)
元素定位,验证刚刚的操作是否成功
有连续的测试方法时,可全局变量赋值,以备后续的测试方式执行(可选)
7、配置说明
{ "LoginUser": { "mobile": "16621376221", "smscode": "376221" }, "headless": false, "timeout": 10000, "pageWidth": 1920, "pageHeight": 1080 }
LoginUser:登录人手机号以及验证码,宪章不支持密码登录,验证码登录之前已经有了特殊处理,如需指定用户可在CommonService.AdminMobileAndSmsCode中添加
headless:调试和运行时是否不弹出浏览器页面,线上环境不用弹出(true),本地环境弹出(false)
timeout:全局超时时间,单位为ms
pageWidth&pageHeight:弹出浏览器页面的宽和高,默认为1080P,运行时不可更改 以上配置在SetUp中的参数中也可以更改,仅影响当前类的测试;
步骤三:UI测试调试与运行
1、本地IDE调试 (Rider、Visual Studio、VS Code)
Rider: 方法左侧点击 或 底部Unit Tests菜单


Visual Studio: 项目栏文件右键菜单 或 底部Test Explorer菜单


VS Code:方法左侧点击(只支持单个方法,不支持整个文件) 或 点击View中的Testing显示Test Explorer菜单
ps:首先要在应用商店安装Playwright,然后仅打开UiTest项目即可



2、命令行运行
- cd到宪章文件夹
- dotnet test YesMro.CS.UiTest -e ENVIRONMENT=Production

ps:ENVIRONMENT的设置是为了区分线上和本地,主要区别在于线上不用开启浏览器,纯内核运行,本地为了调试方便开了浏览器;不加-e即为本地运行;
3、与线上发布闭环
需等待系统发布完成后,等待几分钟才能运行UI测试;
CI运行:https://playwright.dev/dotnet/docs/ci
CodeGen:自动录制生成代码
官方文档:https://playwright.dev/dotnet/docs/codegen#recording-a-test
启动录制:
- 打开PowerShell
- CD到UI项目文件夹
- pwsh bin/Debug/net6.0/playwright.ps1 codegen https://ows.yesmro.cn/home
打开后随着鼠标操作,右侧会有相应的代码生成,可将此代码作为草稿复制出来,随后再更改;
鼠标光标放在UI元素上时会显示相应的定位代码,对于难以定位的元素可以使用这种方式生成;

ps: VS Code支持点击即可录制,但是它只认从VS Code上创建的Playwright项目,而且VS Code上创建时没有c#可选,只能创建支持js、ts的前端语言,从其他IDE创建的c#项目在VS Code可以运行,但是无法点击录制;
有兴趣的可以试下,如果可以录制c#的话,对于轻量化录制有帮助,不需要通过记住PowerShell命令来实现;


浙公网安备 33010602011771号