编写Avocado测试
编写Avocado测试
现在我们开始使用python编写Avocado测试,测试继承于avocado.Test.
基本例子
创建一个时间测试,sleeptest,测试非常简单,只是sleep一会:
import time
from avocado import Test
class SleepTest(Test):
def test(self):
sleep_length = self.params.get('sleep_length', default=1)
self.log.debug("Sleep for %.2f seconds", sleep_length)
time sleep(sleep_length)
这是为avocado编写的一个简单示例,可以使用其他power API编写。
从上面的例子可以看出,avocado测试从avocado.Test继承的test方法入手。
多个测试和命名约定
可以在一个类中存在多个测试。
要想做多个测试,只需要在命名方法前加test,例如test_foo,test_bar等。建议遵守这个命名风格,就像PEP8 Function Names.
对于class名称,可以任意命名,当最好还是遵从CamelCase命名,参考PEP8 中Class Names
方便属性
测试类提供了一定数量的方便属性:
- 为测试添加测试log,可以使用self.log.可以定义log debug,info,error和warning信息。
- 参数解析系统,可以访问self.parms使用。可以在后面的Test variants - Mux文章查看更多信息。
保存生成的数据
每个测试都提供一个所谓的whiteboard,可以通过self.whiteboard访问。Witeboard是一个可以自动保存到测试结果(可以支持的output格式)字符串。你同样可以选择保存二进制数据到whiteboard,但你需要先进行编码转换(base64).
基于上面的sleeptest,假设想保存sleep_length用于其他脚本或数据分析工具:
def test(self):
sleep_length = self.params.get('sleep_length', default=1)
self.log.debug("Sleeping for %.2f second", sleep_length)
time.sleep(sleep_length)
slef.writeboard = "%.2f" % sleep_length
whiteboard可以并应当接收由可用的测试结果插件生成的文件。Result.json文件已经为每个测试包含了whiteboard.此外,会用一个命名为whiteboard的文件保存和results.json相同级别的信息(也许你想使用与你定制的脚本配套的结果处理工具,该文件会保存白板内容的原始副本)。
访问测试参数
每个测试都有一组可以通过self.params.get($name, $path=None, $default=None)访问的参数.Avocado会从Multiplex 配置文件中发现所有参数并填充到self.params.比如上面的例子,sleeptest的multiplex文件:
sleeptest:
type: "builtin"
length: !Mux
short:
sleep_length: 0.5
medium:
sleep_length: 1
long:
sleep_length: 5
当我们用avocado run $test --mux-yaml $file.yaml运行这个例子,3个variants会执行并且内容会插入/run的命名空间。每个variant包含变量的“type"和"sleep_length"。要获取有效值,需要名称(”sleep_length)和path。需要选择路径 ,在这个例子:/run/sleeptest/length/或sleeptest/,取决于如何设置。
默认的参数是可选的,但是要处理好这部分。可能有人运行你的测试时选择不同的参数或没有参数,它仍然能够很好的运行。
所以关于如何访问"sleep_length"的完整示例:
self.params.get("sleep_length", "/*/sleeptest/*", 1)
还有一种更简单的方式。它用于定义解析顺序,简单的查询可以简单的指定路径:
self.params.get("sleep_length", None, 1)
self.params.get("sleep_length", '*', 1)
self.params.get("sleep_length", default=1)
应该避免参数冲突(不同的匹配值应该指定不同的路径)。如果不能这样(例如使用复合yaml文件)可以通过 --mux-path进行默认路径的修改。它将对参数和操作通过路径进行一个接一个的切片。当第一个切片匹配后会返回,不再尝试其他切片。虽然相对查询只匹配来自--mux-path的切片。
有很多方式使用paths分离存在冲突的params或只是使查询更清晰。通常tests中使用"*"就足够了,namespacing不是必须的,但是有助于高级用法更清晰简单。
关于路径的考虑基本是从用户角度。更多信息参考The variants-Mux
使用multplex文件
Avocado可以使用multplex文件提供params和生成generation来运行sleeptest:
$ avocado run sleeptest.py --mux-yaml examples/tests/sleeptest.py.date/sleeptest.yaml
JOB ID : d565e8dec576d6040f894841f32a836c751f968f
JOB LOG : $HOME/avocado/job-results/job-2014-08-12T15.44-d565e8de/job.log
TESTS : 3
(1/3) sleeptest.py:SleepTest.test;1: PASS (0.50 s)
(2/3) sleeptest.py:SleepTest.test;2: PASS (1.00 s)
(3/3) sleeptest.py:SleepTest.test;3: PASS (5.00 s)
RESULTS : PASS 3 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0
TESTS TIME : 6.50 s
JOB HTML : $HOME/avocado/job-results/job-2014-08-12T15.44-d565e8de/html/results.html
The --mux-yaml accepts either only \(FILE_LOCATION or $INJECT_TO:\)FILE_LOCATION. As explained in Test variants - Mux without any path the content gets injected into /run in order to be in the default relative path location. The \(INJECT_TO can be either relative path, then it’s injected into /run/\)INJECT_TO location, or absolute path (starting with '/'), then it’s injected directly into the specified path and it’s up to the test/framework developer to get the value from this location (using path or adding the path to mux-path). To understand the difference execute those commands:
$ avocado multiplex -t -m examples/tests/sleeptest.py.data/sleeptest.yaml
$ avocado multiplex -t -m duration:examples/tests/sleeptest.py.data/sleeptest.yaml
$ avocado multiplex -t -m /my/location:examples/tests/sleeptest.pt.data/sleeptest.yaml
注意,因为multiplex文件为sleeptest指定了所有参数,所以不能将ID留空:
$ scripts/avocado run --mux-yaml examples/tests/sleeptest/sleeptest.yaml
Empty test ID. A test path or alias must be provided
可以使用同一个multiplex文件启动多个测试:
$avocado run sleeptest.py synctest.py --mux-yaml examples/tests/sleeptest.py.data/sleeptest.yaml
JOB ID : cd20fc8d1714da6d4791c19322374686da68c45c
JOB LOG : $HOME/avocado/job-results/job-2016-05-04T09.25-cd20fc8/job.log
TESTS : 8
(1/8) sleeptest.py:SleepTest.test;1: PASS (0.50 s)
(2/8) sleeptest.py:SleepTest.test;2: PASS (1.00 s)
(3/8) sleeptest.py:SleepTest.test;3: PASS (5.01 s)
(4/8) sleeptest.py:SleepTest.test;4: PASS (10.00 s)
(5/8) synctest.py:SyncTest.test;1: PASS (2.38 s)
(6/8) synctest.py:SyncTest.test;2: PASS (2.47 s)
(7/8) synctest.py:SyncTest.test;3: PASS (2.46 s)
(8/8) synctest.py:SyncTest.test;4: PASS (2.45 s)
RESULTS : PASS 8 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0
TESTS TIME : 26.26 s
JOB HTML : $HOME/avocado/job-results/job-2016-05-04T09.25-cd20fc8/html/results.html
高级日志功能
Avocado 在测试的同时提供高级日志功能。这些可以和标准python API库组合。
一个常见的例子是需要在更长或更复杂的测试中遵循具体的进展。 让我们看一个非常简单的测试示例,但在单个测试中有一个多个明确的阶段:
import logging
import time
from avocado import Test
progress_log = logging.getLogger("progress"
class Plant(Test):
def test_plant_organic(self):
rows = self.params.get("rows", default=3)
#Preparing soil
for row in range(rows):
progress_log.info("%s: Preparing soil on row %s",
self.name, row)
# Letting soil rest
progress_log.info("%s: letting soil rest before throwing seeds",
self.name)
time.sleep(2)
# Throwing seeds
for row in range(rows):
progress_log.info("%s: throwing seeds on row %s",
self.name, row)
# Let them grow
progress_log.info("%s: waiting for Avocados to grow",
self.name)
time.sleep(5)
# Harvest them
for row in range(rows):
progress_log.info("%s: harvesting organic avocados on row %s",
self.name, row)
这时,可以请求avocaod显示你的日志流,独占或则加入其他内建流:
$ avocado --show app, progress run plant.py
结果类似于:
JOB ID : af786f86db530bff26cd6a92c36e99bedcdca95b
JOB LOG : /home/cleber/avocado/job-results/job-2016-03-18T10.29-af786f8/job.log
TESTS : 1
(1/1) plant.py:Plant.test_plant_organic: progress: 1-plant.py:Plant.test_plant_organic: preparing soil on row 0
progress: 1-plant.py:Plant.test_plant_organic: preparing soil on row 1
progress: 1-plant.py:Plant.test_plant_organic: preparing soil on row 2
progress: 1-plant.py:Plant.test_plant_organic: letting soil rest before throwing seeds
-progress: 1-plant.py:Plant.test_plant_organic: throwing seeds on row 0
progress: 1-plant.py:Plant.test_plant_organic: throwing seeds on row 1
progress: 1-plant.py:Plant.test_plant_organic: throwing seeds on row 2
progress: 1-plant.py:Plant.test_plant_organic: waiting for Avocados to grow
\progress: 1-plant.py:Plant.test_plant_organic: harvesting organic avocados on row 0
progress: 1-plant.py:Plant.test_plant_organic: harvesting organic avocados on row 1
progress: 1-plant.py:Plant.test_plant_organic: harvesting organic avocados on row 2
PASS (7.01 s)
RESULTS : PASS 1 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0
TESTS TIME : 7.01 s
JOB HTML : /home/cleber/avocado/job-results/job-2016-03-18T10.29-af786f8/html/results.html
自定义的progress流和app输出混合在一起,可能不太适合。如果想让progress流输入到一个独立文件,这样会清晰。
$ avocado run plant.py --store-loggin-stream progress
这样,除了所有其他日志文件通常生成,会有另一个名为日志文件progress.INFO在作业结果目录。 在测试运行期间,可以观察进度:
$ tail -f ~/avocado/job-results/latest/progress.INFO
10:36:59 INFO | 1-plant.py:Plant.test_plant_organic: preparing soil on row 0
10:36:59 INFO | 1-plant.py:Plant.test_plant_organic: preparing soil on row 1
10:36:59 INFO | 1-plant.py:Plant.test_plant_organic: preparing soil on row 2
10:36:59 INFO | 1-plant.py:Plant.test_plant_organic: letting soil rest before throwing seeds
10:37:01 INFO | 1-plant.py:Plant.test_plant_organic: throwing seeds on row 0
10:37:01 INFO | 1-plant.py:Plant.test_plant_organic: throwing seeds on row 1
10:37:01 INFO | 1-plant.py:Plant.test_plant_organic: throwing seeds on row 2
10:37:01 INFO | 1-plant.py:Plant.test_plant_organic: waiting for Avocados to grow
10:37:06 INFO | 1-plant.py:Plant.test_plant_organic: harvesting organic avocados on row 0
10:37:06 INFO | 1-plant.py:Plant.test_plant_organic: harvesting organic avocados on row 1
10:37:06 INFO | 1-plant.py:Plant.test_plant_organic: harvesting organic avocados on row 2
同一个progress日志记录器,可以在多个测试方法和在多个测试模块一起使用。 在给出的示例中,测试名称用于给出额外的上下文。