nginx

Nginx: [emerg] bind() to 0.0.0.0:xxxx failed (13: Permission denied)

安装nginx后,启动的时候报错nginx: [emerg] bind() to 0.0.0.0:8100failed (13: Permission denied)。从字面意思上来说,这是权限不足。但出现这种错误可能有多种原因导致的

端口小于1024时
当端口小于1024时,且运行的账号不是root就会报这个错误。
解决办法是修改nginx的配置文件,以user root;启动

端口大于1024时
检查一下selinux是否开启了。如果开启了关闭selinux试试

setenforce 0
如果关了selinux能正常说明端口可能与selinux的端口冲突了或http_port_t中没有开放对应的端口

1
2
3
4
5
6
7
8
geneve_port_t                  tcp      6080
#发现8100是geneve_port_t 的端口,要么修改自己的端口,要么修改geneve_port_t的端口,然后再把8100添加到http_port_t。附senamage 端口操作命令
#senamage 端口 删除 类型 [] 协议 TCP/UDP 端口
semanage port -d -t geneve_port_t -p tcp 6081
#senamage 端口 增加 类型 [] 协议 TCP/UDP 端口
semanage port -a -t http_port_t -p tcp 6080
#senamage 端口 修改 类型 [] 协议 TCP/UDP 端口
semanage port -m -t geneve_port_t -p tcp 6081
阅读全文
PageObject模式

概述

⽤Page Object表⽰UI,减少重复样板代码,让变更范围控制在Object内,本质是⾯向抽象编程

原则

⽅法意义

⽤公共⽅法代表UI所提供的功能
⽅法应该返回其他的PageObject或者返回⽤于断⾔的数据
同样的⾏为不同的结果可以建模为不同的⽅法
不要在⽅法内加断⾔

字段意义

不要暴露页⾯内部的元素给外部
不需要建模UI内的所有元素

组成

Page对象:完成对页⾯的封装
Driver对象:完成对web、android、ios、接⼜的驱动
测试⽤例:调⽤Page对象实现业务并断⾔
数据封装:配置⽂件和数据驱动
Utils:其他功能封装,改进原⽣框架不⾜

数据驱动

定位符数据驱动
⾏为流数据驱动
断⾔数据驱动
不要全部框架数据驱动,会丢失代码⽐较重要的重构、建模、开放的⽣态能⼒

阅读全文
android常用

adb

常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
adb devices
adb kill-server
adb tcpip
adb connect
adb logcat
adb bugreport
# 查看资源信息
adb shell dumpsys -l
# 查看当前包名
adb shell dumpsys activity
# app信息
adb shell dumpsys activity top # 获取当前界⾯元素
adb shell dumpsys activity activities # 获取任务列表
# app⼊口
adb logcat |grep -i displayed
aapt dump badging mobike.apk | grep launchable-activity
apkanalyzer
# 启动应⽤
adb shell am start -W -n com.xueqiu.android/.view.WelcomeActivityAlias -S
# 查看当前活动页面
adb shell dumpsys activity | grep mFocusedActivity
adb shell dumpsys activity activities|grep realActivity
adb logcat | grep -i displayed
# dump当前页面信息
adb shell "uiautomator dump && cat /sdcard/window_dump.xml"
# x浏览器主页面点击主菜单
adb shell input tap $(adb shell "uiautomator dump --compressed && cat /sdcard/window_dump.xml" | sed 's\<node\^<node>\g' | awk 'BEGIN{RS="^"}{print $0}' | grep 主菜单 | awk 'BEGIN{FS=",|\\[|\\]"}{print ($2+$5)/2,($3+$6)/2}')

# 获取所有的dumpsys⼦命令
dumpsys |grep -i DUMP
# 获取当前activity
adb shell dumpsys activity top
# 获取activities的记录,可以获取到appium依赖的原始activity
adb shell dumpsys activity activities
# 获取特定包基本信息
adb shell dumpsys package com.mmbox.xbrowser
# 获取系统通知
adb shell dumpsys notification
# 获得内存信息
adb shell dumpsys meminfo com.android.settings
# 获取cpu信息
adb shell dumpsys cpuinfo
# 获取gpu绘制分析
adb shell dumpsys gfxinfo com.android.settings
# 获取短信
adb shell dumpsys activity broadcasts | grep senderName=
1
2
3
4
5
6
# 获取内存信息
adb shell dumpsys meminfo com.mmbox.xbrowser
# 一秒滑动一次
while true;do adb shell input swipe 270 850 240 320 1000;done
# app内存占用图形化
for i in $(seq 20); do adb shell dumpsys meminfo com.mmbox.xbrowser | grep "Dalvik Heap" | awk '{print $4}'; sleep 1; done | gnuplot -e "set terminal dumb;plot '<cat' using 1 with line"

模拟器

1
2
3
4
5
# 进⼊emulator所在⽬录
emulator -list-avds
emulator @avd
(cd $(dirname $(which emulator));emulator
@Nexus_6P_API_23;)

func

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 点击
click(){
local index=1
[ -n "$2" ] && index=$2
adb shell input tap $(\
adb shell "uiautomator dump --compressed && cat /sdcard/window_dump.xml" \
| sed 's\<node\^<node>\g' \
| awk 'BEGIN{RS="^"}{print $0}' \
| grep "$1" \
| sed -n "$index"p \
| awk 'BEGIN{FS=",|\\[|\\]"}{print ($2+$5)/2,($3+$6)/2}'\
)
}
# x浏览器试验
for kw in X浏览器 主菜单 历史;do click $kw;done

# 输入
input(){
adb shell input test $1
}

# 滑动
swipe(){
width=$(adb shell wm size | grep -oE "[0-9]*" | head -1)
heigth=$(adb shell wm size | grep -oE "[0-9]*" | tail -1)
adb shell input swipe $(awk 'BEGIN{print '$width'*'$1'}') $(awk 'BEGIN{print '$heigth'*'$2'}') $(awk 'BEGIN{print '$width'*'$3'}') $(awk 'BEGIN{print '$heigth'*'$4'}') 1000
}

scrcpy

https://github.com/Genymobile/scrcpy

实体键映射

切换全屏模式 Ctrl+f
将窗口大小调整为 1:1(像素完美) Ctrl+g
调整窗口大小以删除黑色边框 Ctrl+ x| 双击¹
点击 HOME Ctrl+ h| 中键单击
点击 BACK Ctrl+ b| 右键单击²
点击 APP_SWITCH Ctrl+s
点击 MENU Ctrl+m
点击 VOLUME_UP Ctrl+ (向上)
点击 VOLUME_DOWN Ctrl+ (下)
点击 POWER Ctrl+p
打开屏幕 右键单击²
将计算机剪贴板粘贴到设备 Ctrl+v
启用/禁用 FPS 计数器(在标准输出上) Ctrl+i
从电脑安装APK 拖放 APK 文件
推送文件到 /sdcard/ 拖放非 APK 文件
阅读全文
windows包管理

scoop

Scoop是一个 Win­dows 包管理工具,类似于 De­bian 的 apt、ma­cOS 的 homebrew。它由开源社区驱动,体验可能是是目前所有 Win­dows 包管理工具中最好的。对开发者来说,包管理器能非常方便的部署开发环境,比如 Python 、Node.js 。而对于像博主这样的普通的计算机使用者来说,可以方便的安装一些常用软件,尤其是开源软件,免去了手动去官网下载的繁琐步骤,而且后续对软件进行升级只需要输入一行命令,非常便捷。

官网:https://scoop.sh/

参考:

https://github.com/lukesampson/scoop/wiki

https://sspai.com/post/52496

https://chawyehsu.com/blog/talk-about-scoop-the-package-manager-for-windows-again

环境要求

1
2
3
4
Windows 7 SP1 + / Windows Server 2008+
PowerShell 5(或更高版本,包括 PowerShell Core)和 .NET Framework 4.5(或更高版本)
Windows 用户名为英文(Windows 用户环境变量中路径值不支持中文字符)
正常、快速的访问 GitHub 并下载资源

安装 Scoop 若Powershell或.NET Franmework版本过旧,更新后重启即可。若不清楚版本号,可Win+R运行powershell,输入以下命令获取版本号。

1
2
$PSVersionTable.PSVersion.Major   #查看Powershell版本
$PSVersionTable.CLRVersion.Major #查看.NET Framework版本

安装scoop

Scoop 默认使用普通用户权限,其本体和安装的软件默认会放在 %USERPROFILE%\scoop(即 C:\Users\用户名\scoop),使用管理员权限进行全局安装 (-g) 的软件在 C:\ProgramData\scoop。如果有自定安装路径的需求,那么要提前设置好环境变量,否则后续再改不是一件容易的事情。

  • 打开 PowerShell
  • 设置用户安装路径
1
2
$env:SCOOP='D:\Scoop'
[Environment]::SetEnvironmentVariable('SCOOP', $env:SCOOP, 'User')
  • 设置全局安装路径(需要管理员权限)
1
2
$env:SCOOP_GLOBAL='D:\Scoop_Global'
[Environment]::SetEnvironmentVariable('SCOOP_GLOBAL', $env:SCOOP_GLOBAL, 'Machine')
  • 设置允许 PowerShell 执行本地脚本
1
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
  • 没安装过 Git 则需要安装。
1
scoop install git
  • 安装 Scoop
1
iwr -useb get.scoop.sh | iex

如果下载scoop的过程中断,那么必须先删除(C:\Users\<user>\scoop)文件夹,再执行以上命令安装。
下载完成后,输入scoop出现如下帮助即安装成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Usage: scoop <command> [<args>]

Some useful commands are:

alias Manage scoop aliases
bucket Manage Scoop buckets
cache Show or clear the download cache
checkup Check for potential problems
cleanup Cleanup apps by removing old versions
config Get or set configuration values
create Create a custom app manifest
depends List dependencies for an app
export Exports (an importable) list of installed apps
help Show help for a command
hold Hold an app to disable updates
home Opens the app homepage
info Display information about an app
install Install apps
list List installed apps
prefix Returns the path to the specified app
reset Reset an app to resolve conflicts
search Search available apps
status Show status and check for new app versions
unhold Unhold an app to enable updates
uninstall Uninstall an app
update Update apps, or Scoop itself
virustotal Look for app's hash on virustotal.com
which Locate a shim/executable (similar to 'which' on Linux)


Type 'scoop help <command>' to get help for a specific command.

基础使用

  • 命令列表
1
2
3
4
5
6
7
8
9
10
11
scoop search <app> # 搜索软件
scoop install <app> # 安装软件
scoop info <app> # 查看软件详细信息
scoop list # 查看已安装软件
scoop uninstall <app> # 卸载软件,`-p`删除配置文件。
scoop update # 更新 scoop 本体和软件列表
scoop update <app> # 更新指定软件
scoop update * # 更新所有已安装的软件
scoop checkup # 检查 scoop 的问题并给出解决问题的建议
scoop help # 查看命令列表
scoop help <command> # 查看命令帮助说明

进阶使用

添加 bucket

所有的包管理器都会有相应的软件仓库 ,而 bucket 就是 Scoop 中的软件仓库。细心的你可能会发现 scoop 翻译为中文是 “舀”,而 bucket 是 “水桶”,所以安装软件可以理解为从水桶里舀水。

Scoop 默认软件仓库(main bucket)软件数量是有限的,但是可以进行额外的添加。通过 scoop bucket known 命令可以查看官方认可的 bucket:

1
2
3
4
5
6
7
8
9
10
11
12
$ scoop bucket known
main
extras
versions
nightlies
nirsoft
php
nerd-fonts
nonportable
java
games
jetbrains

以上官方认可的 bucket 可以通过下面这个命令直接添加:

1
scoop bucket add <bucketname>

extras涵盖了大部分因为种种原因不能被收录进主仓库的常用软件,这个是强推荐添加的。

1
scoop bucket add extras

比如写盘工具 Ru­fus 就在 extras 这个仓库中。

1
scoop install rufus

nerd-fonts包含了美化终端时会用到的 Pow­er­line 字体

1
scoop bucket add nerd-fonts

当添加 nerd-fonts 仓库后可以通过以下命令搜索到所有的字体:

1
scoop search "-NF"

安装字体需要使用管理员权限:

1
sudo scoop install FiraCode-NF

第三方 bucket

添加第三方 bucket

1
scoop bucket add <bucketname> https://github.com/xxx/xxx

从第三方 bucket 中安装软件

1
scoop install <bucketname>/<app>

清理安装包缓存

Scoop 会保留下载的安装包,对于卸载后又想再安装的情况,不需要重复下载。但长期累积会占用大量的磁盘空间,如果用不到就成了垃圾。这时可以使用 scoop cache 命令来清理。

  • scoop cache show - 显示安装包缓存
  • scoop cache rm <app> - 删除指定应用的安装包缓存
  • scoop cache rm * - 删除所有的安装包缓存

如果你不希望安装和更新软件时保留安装包缓存,可以加上 -k--no-cache 选项来禁用缓存:

  • scoop install -k <app>
  • scoop update -k *

删除旧版本软件

当软件被更新后 Scoop 还会保留软件的旧版本,更新软件后可以通过 scoop cleanup 命令进行删除。

  • scoop cleanup <app> - 删除指定软件的旧版本
  • scoop cleanup * - 删除所有软件的旧版本

与安装软件一样,删除旧版本软件的同时也可以清理安装包缓存,同样是加上 -k 选项。

  • scoop cleanup -k <app> - 删除指定软件的旧版本并清除安装包缓存
  • scoop cleanup -k * - 删除所有软件的旧版本并清除安装包缓存

全局安装

全局安装就是给系统中的所有用户都安装,且环境变量是系统变量,对于需要设置系统变量的一些软件就需要全局安装,比如 Node.js、Python ,否则某些情况会出现无法找到命令的问题。

使用 scoop install <app> 命令加上 -g--global 选项可对软件进行全局安装,全局安装需要管理员权限,所以需要提前以管理员权限运行的 Pow­er­Shell 。更简单的方式是先安装 sudo,然后用 sudo 命令来提权执行:

1
2
scoop install sudo
sudo scoop install -g <app>

达成在 Win­dows 上使用sudo的成就

使用 scoop list 命令查看已装软件时,全局安装的软件末尾会有 *global* 标志。

1
2
3
4
5
6
7
8
9
10
$ scoop list
Installed apps:

7zip 19.00
ffmpeg 4.2.3
git 2.26.2.windows.1 *global*
nodejs-lts 12.17.0 *global*
python 3.8.3 *global*
screentogif 2.24.2 [extras]
sudo 0.2020.01.26

此外对于全局软件的更新和卸载等其它操作,都需要加上 -g 选项:

1
2
3
4
5
sudo scoop update -g * # 更新所有软件(且包含全局软件)
sudo scoop uninstall -g <app> # 卸载全局软件
sudo scoop uninstall -gp <app> # 卸载全局软件(并删除配置文件)
sudo scoop cleanup -g * # 删除所有全局软件的旧版本
sudo scoop cleanup -gk * # 删除所有全局软件的旧版本(并清除安装包包缓存)

代理设置

Scoop 默认使用的是系统代理,如果你想手动指定代理,可以输入下面的命令。需要注意的是只支持 http 协议。

1
scoop config proxy localhost:1080

设置完可以通过scoop config proxy查看。

如果你想取消代理,那么输入下面的命令,这将会恢复使用系统代理。

1
scoop config rm proxy

开启多线程下载

使用 Scoop 安装 Aria2 后,Scoop 会自动调用 Aria2 进行多线程加速下载。

1
scoop install aria2

使用 scoop config 命令可以对 Aria2 进行设置,比如 scoop config aria2-enabled false 可以禁止调用 Aria2 下载。以下是与 Aria2 有关的设置选项:

  • aria2-enabled: 开启 Aria2 下载,默认true
  • aria2-retry-wait: 重试等待秒数,默认2
  • aria2-split: 单任务最大连接数,默认5
  • aria2-max-connection-per-server: 单服务器最大连接数,默认5 ,最大16
  • aria2-min-split-size: 最小文件分片大小,默认5M

这里推荐以下优化设置,单任务最大连接数设置为 32,单服务器最大连接数设置为 16,最小文件分片大小设置为 1M

1
2
3
scoop config aria2-split 32
scoop config aria2-max-connection-per-server 16
scoop config aria2-min-split-size 1M

常用命令总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 更新 scoop 及软件包列表
scoop update

## 安装软件 ##
# 非全局安装(并禁止安装包缓存)
scoop install -k <app>
# 全局安装(并禁止安装包缓存)
sudo scoop install -gk <app>

## 卸载软件 ##
# 卸载非全局软件(并删除配置文件)
scoop uninstall -p <app>
# 卸载全局软件(并删除配置文件)
sudo scoop uninstall -gp <app>

## 更新软件 ##
# 更新所有非全局软件(并禁止安装包缓存)
scoop update -k *
# 更新所有软件(并禁止安装包缓存)
sudo scoop update -gk *

## 垃圾清理 ##
# 删除所有旧版本非全局软件(并删除软件包缓存)
scoop cleanup -k *
# 删除所有旧版本软件(并删除软件包缓存)
sudo scoop cleanup -gk *
# 清除软件包缓存
scoop cache rm *

Chocolatey

Chocolatey是一款专为Windows系统开发的、基于NuGet的包管理器工具,类似于Node.js的npm,MacOS的 brew,Ubuntu的 apt-get,简称为 choco。
Chocolatey的设计目标是成为一个去中心化的框架,便于开发者按需快速安装应用程序和工具。

Chocolatey 官网: https://chocolatey.org/

软件源:https://community.chocolatey.org/packages

github:https://github.com/chocolatey/choco

参考:

https://guangchuangyu.github.io/cn/2018/06/chocolatey/

安装

使用以管理员权限打开PowerShell

运行 Get-ExecutionPolicy,如果结果是 Restricted,则运行 Set-ExecutionPolicy AllSigned 或者 Set-ExecutionPolicy Bypass -Scope Process

输入并执行如下命令:

1
Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

或者

1
iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))

Chocolatey的作者创建了一个短网址,包含了Chocolatey的安装脚本。嫌上面的太长的话,可以用这个:

1
iwr https://cin.st | iex

自定义安装位置

以上都是安装到了默认位置C:\ProgramData\Chocolatey,如果现在其他地方安装Chocolatey的话,需要新建一个环境变量ChocolateyInstall,值是你希望安装到的文件夹。然后手动创建这个文件夹。然后再运行上面的命令。如果已经安装了,可以再次运行安装命令,这样可以重新安装到你指定的位置。

包列表

通过官网 https://chocolatey.org/packages 可以图形界面浏览各软件包,以及安装命令

使用

  • search - 搜索包 choco search something
  • list - 列出包 choco list -lo
  • install - 安装 choco install baretail
  • pin - 固定包的版本,防止包被升级 choco pin windirstat
  • upgrade - 安装包的升级 choco upgrade baretail
  • uninstall - 安装包的卸载 choco uninstall baretail

更多命令可以通过 choco -help 查看

1
2
3
4
5
6
7
8
9
10
11
12
choco install autohotkey.portable    #安装 AutoHotkey (Portable)
choco install nodejs.install #安装 node
choco install git.install #安装 git
choco install python #安装 python
choco install ruby #安装 ruby
choco install jdk8 #安装 JDK8
choco install googlechrome #安装 Chrome
choco install google-chrome-x64 #Google Chrome (64-bit only)
choco install firefox #安装 firefox
choco install notepadplusplus.install #安装 notepad++
choco install Atom #安装 Atom
choco install SublimeText3 #安装 SublimeText3

Winget

官方文档:https://docs.microsoft.com/zh-cn/windows/package-manager/winget/

github:https://github.com/microsoft/winget-cli

安装

  • 法1:提交 WinGet 的预览体验申请,Win10 商店下载

  • 法2:申请加入 Windows Insider,Win10 商店下载

  • 法3:GitHub下载安装包直接安装

推荐法3,简单直接, 在 GitHub 搜索 WinGet,microsoft/winget-cli 就是该项目。在 releases 页面下载名为 Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.appxbundle 的软件安装包。

使用

安装 应用安装程序 后,可以通过在命令提示符下键入 “winget” 来运行 WinGet 。最常见的使用场景之一是搜索并安装你最喜欢的工具。若要搜索某个工具,请键入 winget search \<appname>

确认你需要的工具可用后,可以通过键入 winget install \<appname> 来安装该工具。 WinGet 工具会启动安装程序,将应用程序安装在你的电脑上。

命令 说明
hash 为安装程序生成 SHA256 哈希。
help 显示 winget 工具命令的帮助信息。
install 安装指定的应用程序。
search 搜索某个应用程序。
show 显示指定应用程序的详细信息。
source 添加、删除和更新 winget 工具访问的 Windows 程序包管理器存储库。
validate 验证要提交到 Windows 程序包管理器存储库的清单文件。
阅读全文
自动化测试相关工具简介

Framework and tools

测试框架:Unittest/Pytest/nose、Allure;
接口⾃动化测试:Requests/HttpRunner
app/web⾃动化测试:Appium,Selenium
性能测试:locust
设计模式:PageObject(POM)
测试开发:Django,Flask
代码管理 Git
代码分析 FindBugs Sonar
单元测试 JUnit
持续集成管理 Jenkins
⾃动化环境构建:Docker
⾃动化测试、研发、预发布环境管理:Chef、Puppet、Docker

service

python cgi

1
2
3
# 需要创建cgi-bin目录
python2 -m CGIHTTPServer 8080
python3 -m http.server --cgi 8000

模拟服务

moco:https://github.com/dreamhead/moco

Third party library

断言库

jsonpath
hamcrest
jsonschema

http断言:BeautifulSoup

1
2
pip install bs4
pip install lxml

通用参数化

1
pip install parameterized

模板技术

template
nustache
jinja2

测开工具

依赖管理

poetry

CI

travisCI

python代码覆盖coveralls报告

coverage

命令行支持

argparse包

阅读全文
元素定位
描述 Xpath CSS Path
直接子元素 //div/a div > a
子元素或后代元素 //div//a div a
以id定位 //div[@id=’idValue’]//a div#idValue a
以class定位 //div[@class=’classValue’]//a div.classValue a
同级弟弟元素 //ul/li[@class=’first’]/following-sibling::li ul>li.first + li
属性 //form/input[@name=’username’] form input[name=’username’]
多个属性 //input[@name=’continue’ and @type=‘button’] input[name=’continue’][type=’button’]
第4个子元素 //ul[@id=’list’]/li[4] ul#list li:nth-child(4)
第1个子元素 //ul[@id=’list’]/li[1] ul#list li:first-child
最后1个子元素 //ul[@id=’list’]/li[last()] ul#list li:last-child
属性包含某字段 //div[contains(@title,’Title’)] div[title*=”Title”]
属性以某字段开头 //input[starts-with(@name,’user’)] input[name^=”user”]
属性以某字段结尾 //input[ends-with(@name,’name’)] input[name$=”name”]
text中包含某字段 //div[contains(text(), ‘text’)] 无法定位
元素有某属性 //div[@title] div[title]
父节点 //div/.. 无法定位
同级哥哥节点 //li/preceding-sibling::div[1] 无法定位

xpath

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 绝对定位 
/xxx/ddd/dddd
#相对定位
//android.widget.Button
# 查找所有元素:
//*
# 包含条件:
//*[contains(@resource-id, ‘login’)]
# 满⾜属性值
//*[@text=‘登录’]
# 多条件联合
//*[contains(@resource-id, ‘login’) and contains(@text, ‘登录’)]]
//*[contains(@text, ‘登录’) or contains(@label, ‘登录’)]]
# 当多控件同时出现
//*[contains(@text, '看点')]/ancestor::*//*[contains(name(), ‘EditText’)]
# 根据可点击属性和⽂本内容长度筛选
//*[@clickable="true"]//android.widget.TextView[stringlength(@
text)>0 and string-length(@text)<20]


//*[@id="select_baseUrl"]/option
$x('//*[@id="select_baseUrl"]//option[@value]')[0].value

//*[android.widget.EditText]
//*[@class='android.widget.EditText' and contains(@content-desc, '')]
# toast识别条件
# automationName:uiautomator2
getPageSource是⽆法找到的,必须使⽤xpath查找
//*[@class='android.widget.Toast']
//*[contains(@text, "xxxxx")]
$x('//*[contains(text(),"B10资源参数")]')
$x('//div[contains(text(),"B10资源参数")]')

CSS

1
2
$('span[class="e_ico-add"]')

阅读全文
Linux三剑客
grep | sed | awk

example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#点赞人数统计
a=$(curl -s https://testerhome.com/topics|grep -o 'href="/topics/[0-9]*"'|awk -F '/|"' '{print $4}');for id in $a;do url='https://testerhome.com/topics/'$id;zan=$(curl -s $url|grep -o -m1 '<span>[0-9]*'|awk -F '>' '{print $2}');if [ -n "$zan" ];then echo $url '点赞人数' $zan;else echo $url '点赞人数' 0;fi;done|awk -F '/' '{print $NF}'

echo -e "1|2|3\n4|5|6\n7|8|9"|awk -F '|' 'BEGIN{a=0}{a=a+$2}END{print a}'

curl -s https://www.baidu.com/ |grep href |grep -o "http[^>]*" | while read line;do curl -s -I $line | grep 200 &>/dev/null && echo 200 $line || echo ERR $line;done

curl https://testerhome.com/api/v3/topics.json 2>/dev/null | sed 's/"title":"[^"]*/title":"xxxxxx"/g'


#统计nginx日志访问量最高
awk {'print $7'} nginx.log |awk -F '?' '{print $1}' |sort |uniq -c |sort -n |tail -1 |awk '{print $2}'
#平均耗时
awk {'print $7,$(NF-1)'} nginx.log | grep '^/api/[a-z0-9_]*' |awk '{s+=$2} END{print s/NR}'

awk '$7~/api\/[a-z]+$/{sum+=$(NF-1)}END{print sum/NR}' nginx.log

seq 10|awk '/[5-9]/'
seq 10|awk '/5/,/9/'

#分割
echo '1|2#3_4' | awk -F '#_|_|\\|' '{print $1}'
echo '1|2#3_4' | awk 'BEGIN{FS="#|_|\\|"}{print $1}'
#分割加分割符
echo '1|2#3_4' | awk 'BEGIN{FS="#|_|\\|";OFS="_"}{print $1,$2,$3,$4}'
#分成多行
echo '1|2#3_4' | awk 'BEGIN{RS="#|_|\\|"}{print $1}'
#分成多行拼成一行
echo '1|2#3_4' | awk 'BEGIN{RS="#|_|\\|";ORS="_"}{print $1}'

#awk格式化输出
awk 'BEGIN{printf "%.1f\n",1/3}'
#求和
seq 10 2 20| awk '{sum+=$1}{print sum}'
#求平均
seq 10 2 20| awk '{sum+=$1}{print sum/NR}'
#输出大于14的
seq 10 2 20| awk '$1>14{data[NR]=$1}END{for(k in data)print data[k]}'
#多点编辑(取出后两位)
seq 125 137|sed -E 's#.(.)(.)#\1+\2#g'

#根据pid查看20秒内程序cpu,mem使用信息
for i in $(seq 1 20);do time=$(date +%H:%M:%S);cpu=$(ps -o %cpu -o %mem pid| tail -1);echo $time $cpu;sleep 1;done

#并发
while true; do count=$(jobs -l |grep Running | wc -l);[ $count -le 2 ] && { time curl https://www.baidu.com &>/dev/null & } || echo $count waiting;done

ps -o uname,pid,ppid,thcount,ni,pri,psr,pcpu,pmem,rss,vsz,sz,start_time,time,comm,command -e

#20秒内存平均
timeall=0; for i in $(seq 1 20);do time=$(date +%H:%M:%S);MEM=$(ps -o uname,pid,ppid,thcount,ni,pri,psr,pcpu,pmem,rss,vsz,sz,start_time,time,comm,command -p 9446 | awk -F ' *' '{print $9}' | tail -1);echo $time %MEM=$MEM ;timeall=$(awk 'BEGIN{print '$timeall'+'$MEM'}'); awk 'BEGIN{print '$timeall'/'$i'}' ;sleep 1;done

for i in $(seq 1 20);do time=$(date +%H:%M:%S);MEM=$(ps -o uname,pid,ppid,thcount,ni,pri,psr,pcpu,pmem,rss,vsz,sz,start_time,time,comm,command -p 9446 | awk -F ' *' '{print $9}' | tail -1);echo $time %MEM= $MEM ;sleep 1;done | awk '{print $0,t+=$3;print t/NR}'

for i in $(seq 1 20);do time=$(date +%H:%M:%S);MEM=$(ps -o uname,pid,ppid,thcount,ni,pri,psr,pcpu,pmem,rss,vsz,sz,start_time,time,comm,command -p 9446 | awk -F ' *' '{print $9}' | tail -1);echo $time %MEM= $MEM ;sleep 1;done | awk '{print $0;print (t+=$3)/NR}'

#打印cpu 内存
top -b -d 1 -n 20 -p 9446|grep --line-buffered 9446 | awk '{cpu+=$9;mem+=$10}{print $9,$10,cpu/NR,mem/NR}'

#图形化cpu mem
top -b -d 1 -n 20 -p 9446|grep --line-buffered 9446 | awk '{cpu+=$9;mem+=$10}{print $9,$10,cpu/NR,mem/NR}'|gnuplot -e "set terminal dumb;plot '<cat' using 1 with line"
阅读全文
pytest使用及相关插件

编写规范:

• 测试⽂件以 test_ 开头(以 test 结尾也可以)
• 测试类以 Test 开头,并且不能带有 init 方法!
• 测试函数以 test

模块级(setup_module/teardown_module)模块始末,全局的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import pytest
"""
开始于模块始末,全局的
setup_module
teardown_module
"""

def setup_module():
print "setup_module():在模块最之前执行\n"

def teardown_module():
print "teardown_module:在模块之后执行"

def setup_function():
print "setup_function():每个方法之前执行"

def teardown_function():
print "teardown_function():每个方法之后执行\n"

def test_01():
print "正在执行test1"
x = "this"
assert 'h' in x

def add(a,b):
return a+b

def test_add():
print "正在执行test_add()"
assert add(3,4) == 7
#运行结果:setup_module --> setup_function --> test_01--> teardown_function --> setup_function --> test_add()--> teardown_function --> teardown_module

函数级(setup_function/teardown_function)只对函数用例生效,而且不在类中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import pytest

def setup_function():
print "setup_function():每个方法之前执行"

def teardown_function():
print "teardown_function():每个方法之后执行"

def test_01():
print "正在执行test1"
x = "this"
assert 'h' in x

def test_02():
print "正在执行test2"
x = "hello"
assert hasattr(x,"hello")

def add(a,b):
return a+b

def test_add():
print "正在执行test_add()"
assert add(3,4) == 7

if __name__=="__main__":
pytest.main(["-s","test_function.py"])

#运行结果为:(-s为了显示用例的打印信息 -q只显示结果不显示过程)
#可以看出执行的结果是:
#setup_function--》 test_01 --》teardown_function
#setup_function--》 test_02 --》teardown_function
#setup_function--》 test_add --》teardown_function

类级(setup_class/teardown_class)在类中使用,类执行之前运行一次,类执行之后运行一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import pytest

class TestClass(object):

def setup_class(self):
print "setup_class(self):每个类之前执行一次"

def teardown_class(self):
print "teardown_class(self):每个类之后执行一次"

def add(self,a,b):
print "这是加法运算"
return a+b

def test_01(self):
print "正在执行test1"
x = "this"
assert 'h' in x

def test_add(self):
print "正在执行test_add()"
assert self.add(3, 4) == 7

#执行顺序 setup_class --》 test1 --》test_add()--》teardown_class

⽅法级(setup_method/teardown_methond)运⾏在类中⽅法始末

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import pytest

class TestMethod(object):

def setup_class(self):
print "setup_class(self):每个类之前执行一次\n"

def teardown_class(self):
print "teardown_class(self):每个类之后执行一次"

def setup_method(self):
print "setup_method(self):在每个方法之前执行"

def teardown_method(self):
print "teardown_method(self):在每个方法之后执行\n"

def add(self,a,b):
print "这是加法运算"
return a+b

def test_01(self):
print "正在执行test1"
x = "this"
assert 'h' in x

def test_add(self):
print "正在执行test_add()"
assert self.add(3, 4) == 7
#执行结果: setup_class --》 setup_method -->test1 -->teardown_method --》setup_method --> test_add()--》teardown_method --> teardown_class

执行

pytest/py.test(终端,命令⾏,pycharm都⾏,可配置pycharm使⽤pytest⽅式执⾏)
pytest –v (最⾼级别信息—verbose)
pytest -v -s ⽂件名 (s是带控制台输出结果,也是输出详细)
pytest将在当前⽬录及其⼦⽬录中运⾏test _ * .py或* test.py形式的所有⽂件。
以test_开头的函数,以Test开头的类,以test_开头的⽅法。所有包package都要有init.py⽂件。
Pytest可以执⾏unittest框架写的⽤例和⽅法

1
2
3
4
5
6
7
8
9
10
11
12
# 执行一个module
pytest -v src/testcases/api/test_autouse.py
# 执⾏⼀个类,一个方法
pytest -v src/testcases/api/test_autouse.py::TestSample
pytest -v src/testcases/api/
test_autouse.py::TestSample::test_answer
# 执⾏⼀个目录或者package
pytest -v src/testcases/api!
# 通过标签来运行测试用例
pytest -m P0 src/testcases/api/
# 通过pytest.main来执行,所有的参数和pytest命令行方式一样
pytest.main(['-v', '--instafail', 'testcases/api/test_example.py', '-m=P2'])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# pytest执⾏⽤例出错时停⽌
pytest -x -v -s test.py --maxfail=2
# pytest通过⽂件名类名⽅法及组合调⽤部分⽤例执
# 直接输⼊⽂件名,类名
pytest test_class_01.py
pytest -v -s test_class_01.py
pytest -v test_class_01.py::TestClass
pytest -v test_class_01.py::TestClass::test_one
# 使⽤-k TestClass是类名,and是运算符,还可以是and not…,test_one是⽅法名中含有的信息
pytest -k "TestClass and test_one"
pytest -k "TestClass or test_one"
# mark标记执⾏
# 在测试⽤例⽅法上加@pytest.mark.webtest,-s参数: 输出所有测试⽤的print信息 -m:执⾏⾃定义标记的相关⽤例
pytest -s test.py -m=webtest

log

1
2
3
4
5
6
7
8
9
建立这个文件Pytest.ini
[pytest]
log_cli = 1
log_cli_level = info
log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
log_cli_date_format=%Y-%m-%d %H:%M:%S
写测试用例————log.info()
用: pytest pytest_testlog.py -o log.cli=true -o log._cli_level=INFO
-o是覆盖配置的内容

插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# pytest-html ⽣成可视化报告,--self-contained-html(把css样式合并到html)
pytest -v -s --html=report.html --self-contained-html

# pytest-assume 多条断⾔有失败也都运⾏
pytest.assume(1==4)
pytest.assume(2==4)

# pytest-rerunfailures 失败重跑 —reruns n, n是重复次数
pytest --reruns 5 test_sort_demo.py

pytest-allure #⾼⼤上精美报告

pytest-xdist #并⾏运⾏多个测试 pytest -n 2 在2个cpu上运⾏测试 —looponfail标志,它将⾃动重新运⾏你的失败测试
pytest -n 3

Pytest-sugar # 改变pytest的默认外观,增加进度条,安装后即可

Pytest-picked # 运⾏基于你已修改但尚未提交给git的代码的测试。

Pytest-instafail # 修改默认⾏为,以⽴即显⽰失败和错误,⽽不是等到pytest完成每个测试运⾏。

Pytest-tldr # 简化man指令帮助文档

Pytest-django # 开发web

Pytest-selenium # pytest 提供运⾏⽀持selenium为基础

pytest-ordering # 顺序编排
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# fixtures:为了测试⽤例的执行,⽽初始化⼀些数据和方法
# 名字调用,yield 之前相当于setup;yield 之后相当于tearDown
import pytest
@pytest.fixture()
def loginandlogout():
print('do login action\n')
yield
print("do logout action")
class TestSample:
def test_answer(self,loginandlogout):
...
def test_answer2(self,loginandlogout):
...
# decorator调用
import pytest
@pytest.fixture()
def loginandlogout():
print('do login action\n')
yield
print("do logout action")
class TestSample:
@pytest.mark.usefixtures('loginandlogout')
def test_answer(self):
...
# autouse,Fixture scope(module,session,class,package)
@pytest.fixture(scope='module',autouse=True)
def loginandlogout():
print('do login action\n')
yield
print("do logout action\n")
@pytest.fixture(scope='class',autouse=True)
def clickhome():
print('click home button\n')
yield
print("end click home link\n")
class TestSample:
def test_answer(self):
def test_answer2(self):
class TestSampleTwo:
def test_two_answer(self):
# 带参数传递
@pytest.fixture(params=[1, 2, 3, 'bq'])
# mark.parametrize参数化
@pytest.mark.parametrize()
# 参数化和数据驱动
@pytest.mark.parametrize(“login_r”,test_user_data, indirect=True) indeirect=True是把login_r当作函数去执⾏
# mark中的skip与xfail
# 跳过这个测试⽤例,可以加条件skipif,在满⾜某些条件下才希望通过,否则跳过这个测试。
@pytest.mark.skip
# 测试通过时尽管预计会失败(标记为pytest.mark.xfail),它是⼀个xpass,将在测试摘要中报告。
@pytest.mark.xfail


# Conftest.py:定义共享的fixture,一般放在testcases目录下,每个子目录下的conftest.py中的fixture优先

钩子函数

获取用例执行结果处理用例依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# conftest.py

import pytest

# 定义一个类,来存储用例执行失败的结果
class Falied:
skip = False


@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_makereport():
result = yield
report = result.get_result()

# 当前用例名
case_name = report.nodeid.split('::')[-1]

# 当某个用例失败时(setup失败也算)或被跳过时,将这个用例的执行结果存储在 Failed 类中
if report.when in ('setup', 'call') and report.outcome in ('failed', 'skipped'):
setattr(Falied, case_name, True)

if __name__ == '__main__':
pytest.main(['-s', '-q'])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pytest
from .conftest import Falied


class TestDemoA:

def test_A_001(self):
# 断言失败
assert 0

def test_A_002(self):
# 从 Failed 获取 test_A_001 的执行结果
if getattr(Falied, 'test_A_001', False):
pytest.skip('test_A_001 执行失败或被跳过,此用例跳过!')

def test_A_003(self):
pass

report

生成 JunitXML 格式的测试报告,命令 : –junitxml= path
生成 ResultLog 格式的测试报告,命令: –resultlog=report/log.txt
生成 Html 格式的测试报告,命令: –html=OutPuts/reports/report.html (相对路径)

1
pytest --html=report/html/report.html  testcase/test_new_outfit.py::TestNewOutfit::test_newoutfit_normal
阅读全文
CentOS7根目录磁盘扩容

CentOS7根目录磁盘扩容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
[root@xycvt99 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/centos-root 17G 17G 12M 100% /
devtmpfs 8.0G 0 8.0G 0% /dev
tmpfs 16G 1.4G 15G 9% /dev/shm
tmpfs 8.1G 106M 7.9G 2% /run
tmpfs 8.1G 0 8.1G 0% /sys/fs/cgroup
/dev/sdb1 197G 52G 135G 28% /opt
/dev/sda1 1014M 145M 870M 15% /boot
tmpfs 1.7G 0 1.7G 0% /run/user/0
tmpfs 1.7G 0 1.7G 0% /run/user/1009
[root@xycvt99 ~]# sudo fuser -km /opt
/opt: 3261ce 3590ce 3863ce 4628ce 9801ce 9829 9835ce 11643ce 11695ce 12835 14394ce 14396ce 14399ce 14401ce 14406ce 14408ce 14412ce 14415ce 14417ce 14420ce 14422ce 14424ce 14426ce 14428ce 14430ce 14432ce 14436ce 14438ce 14441ce 14443ce 14445ce 14447ce 14464ce 14466ce 14468ce 14470ce 14474ce 14476ce 14478ce 14480ce 14482ce 14817ce 14819ce 14822ce 14824ce 14826ce 14829ce 14831ce 15012ce 15026ce 15040ce 15465ce 15846ce 15848ce 15854ce 15856ce 15858ce 15860ce 15862ce 15864ce 15866ce 15868ce 15870ce 15872ce 15878ce 15890ce 15940ce 16429 16435 16437 16439 16442 16444 16446 16448 16563 16587ce 16959ce 16964ce 16981ce 16996 17002 17004 17006 17008 17010 17012 17014 17029ce 17048ce 17065ce 17092ce 17095 17100ce 17106ce 17111ce 17115ce 17935ce 30058 30308ce 30345
[root@xycvt99 ~]# umount /opt
[root@xycvt99 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/centos-root 17G 17G 5.9M 100% /
devtmpfs 8.0G 0 8.0G 0% /dev
tmpfs 16G 52K 16G 1% /dev/shm
tmpfs 8.1G 106M 7.9G 2% /run
tmpfs 8.1G 0 8.1G 0% /sys/fs/cgroup
/dev/sda1 1014M 145M 870M 15% /boot
tmpfs 1.7G 0 1.7G 0% /run/user/0
tmpfs 1.7G 0 1.7G 0% /run/user/1009

[root@xycvt99 ~]# fdisk -l

Disk /dev/sdb: 214.7 GB, 214748364800 bytes, 419430400 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x2294ea50

Device Boot Start End Blocks Id System
/dev/sdb1 2048 419430399 209714176 83 Linux

Disk /dev/sda: 21.5 GB, 21474836480 bytes, 41943040 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x000c339c

Device Boot Start End Blocks Id System
/dev/sda1 * 2048 2099199 1048576 83 Linux
/dev/sda2 2099200 41943039 19921920 8e Linux LVM

Disk /dev/mapper/centos-root: 18.2 GB, 18249416704 bytes, 35643392 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mapper/centos-swap: 2147 MB, 2147483648 bytes, 4194304 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

[root@xycvt99 ~]# lvextend -L +200G /dev/mapper/centos-root
Insufficient free space: 51200 extents needed, but only 0 available
[root@xycvt99 ~]# vgs
VG #PV #LV #SN Attr VSize VFree
centos 1 2 0 wz--n- <19.00g 0
[root@xycvt99 ~]# vgextend centos /dev/sdb
Device /dev/sdb excluded by a filter.
[root@xycvt99 ~]# vgs
VG #PV #LV #SN Attr VSize VFree
centos 1 2 0 wz--n- <19.00g 0
[root@xycvt99 ~]# vgextend centos /dev/sdb1
WARNING: ext4 signature detected on /dev/sdb1 at offset 1080. Wipe it? [y/n]: y
Wiping ext4 signature on /dev/sdb1.
Physical volume "/dev/sdb1" successfully created.
Volume group "centos" successfully extended
[root@xycvt99 ~]# vgs
VG #PV #LV #SN Attr VSize VFree
centos 2 2 0 wz--n- 218.99g <200.00g
[root@xycvt99 ~]# lvextend -L +200G /dev/mapper/centos-root
Insufficient free space: 51200 extents needed, but only 51199 available
[root@xycvt99 ~]# lvextend -L +199G /dev/mapper/centos-root
Size of logical volume centos/root changed from <17.00 GiB (4351 extents) to <216.00 GiB (55295 extents).
Logical volume centos/root successfully resized.
[root@xycvt99 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/centos-root 17G 17G 6.6M 100% /
devtmpfs 8.0G 0 8.0G 0% /dev
tmpfs 16G 52K 16G 1% /dev/shm
tmpfs 8.1G 106M 7.9G 2% /run
tmpfs 8.1G 0 8.1G 0% /sys/fs/cgroup
/dev/sda1 1014M 145M 870M 15% /boot
tmpfs 1.7G 0 1.7G 0% /run/user/0
tmpfs 1.7G 0 1.7G 0% /run/user/1009
[root@xycvt99 ~]# xfs_growfs /dev/mapper/centos-root
meta-data=/dev/mapper/centos-root isize=512 agcount=4, agsize=1113856 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=0 spinodes=0
data = bsize=4096 blocks=4455424, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0 ftype=1
log =internal bsize=4096 blocks=2560, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
data blocks changed from 4455424 to 56622080
[root@xycvt99 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/centos-root 216G 17G 200G 8% /
devtmpfs 8.0G 0 8.0G 0% /dev
tmpfs 16G 52K 16G 1% /dev/shm
tmpfs 8.1G 106M 7.9G 2% /run
tmpfs 8.1G 0 8.1G 0% /sys/fs/cgroup
/dev/sda1 1014M 145M 870M 15% /boot
tmpfs 1.7G 0 1.7G 0% /run/user/0
tmpfs 1.7G 0 1.7G 0% /run/user/1009
阅读全文
Unittest使用

组件

test cases、test suites、test fixtures、test runner

编写规范

• 测试模块首先 import unittest
• 测试类必须继承 unittest.TestCase
• 测试⽅方法必须以“test_”开头!
• 模块名字,类名没有要求

执行规则

基于测试⽅法级别的setUp, tearDown
• 执⾏每个测试方法的时候都会执行一次setUp 和tearDown
• 基于类级别的 setUpClass, tearDownClass
• 执⾏这个类里⾯的所有测试⽅法只有一次执行setup, tearDown
• 基于模块级别的 setUpModule,tearDownModule
• 执⾏此模块里的所有类⾥的测试⽅方法,只执行⼀次setup和teardown

example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
封装测试套件运行某个目录下所有用例并出html报告
importmport os,time
case path = os.path.join(os.path.dirname(_ file_ ), 'demo_unittest')
report path = os.path.join(os.path,dirnane(_ file__ ), 'report')
def create suite( ):
suite = unittest.TestSuite( )
discover = unittestdefaultTestLoader.discover(casepath,pattern='test*.py',top_level_dir=None)
print (discover )
for test suite in discover:
for test case in test_suite :
suite.addTests (test_case )
return suite
now = time.strftime("%Y-%m-%d %H_%M_%S", time.localtime())
filename = report_path+"/”+now+"_result.html"
with open(filename, 'wb') as fp:
runner = HTMLTestRunner(
stream=fp,
title='接口测试报告',
description='搜索用例执行情况:')
runner.run(create_suite())
fp.close()
阅读全文
Algolia