#pyATSとは
pyATS は、Python Automated Test Systems の略で、python3 ベースのテストフレームワークです。Cisco によって開発されています。license は、Apache 2.0 です。
Document などの情報は、以下のリンクにあります。
Cisco DevNet: pyATS
pytest のように、pythonプログラムのテストに使用できますが、Device object や ConnectionManager object という router 等の network device のテストをするための機能も含まれています。
この記事では、とりあえず pyATS の基本機能を動作させてみただけで、Device object などは使っていません。
pyATS で Cisco IOS Router にアクセスした事例は、 「pyATSによるCisco IOS Routerのテスト例」 に記載しています。
#インストール
環境
- pyATS は、python 3.4 以上を必要とします。今回は、3.4.5を使いました。
- python3 の development library が必要です。今回は、python34-devel.x86_64 を使いました。
- CentOS7 上で動かしました。
- pyATS は、4.0.0 を使用しています。
なお、pyATS 4.0.0 は、Linux と Mac OS には対応していますが、Windows 系は未サポートとなっています。
##インストール方法
普通に pip でインストールできます。今回は、以下のように virtualenv で動かしています。
$ python3 -m virtualenv pyATS
$ cd pyATS
$ source bin/activate
$ pip install pyats
#テストをしてみる
##テスト対象
テスト対象として以下のようなクラスを使いました。
#!/usr/bin/env python
class Shape(object):
def __init__(self, name):
self.name = name
if name == 'triangle':
self.sides = 3
elif name == 'rectangle':
self.sides = 4
elif name == 'pentagon':
self.sides = 6
else:
self.sides = 0
##テストスクリプトの構造
テストスクリプトは、ats.aetest モジュールの Class を使って記述します。
pyATS では、スクリプトは以下のような構造になっています。
- 0または1個の CommonSetup Class
- 1個以上の Testcase Class
- 0または1個の CommonCleanup Class
CommonSetup と CommonCleanup は、複数の subsection を method として持ちます。
Testcase は更に以下の sub section に分かれます。
- 0または1個の setup method
- 1個以上の test method
- 0または1個の cleanup method
それぞれ、@setup, @test, @cleanup
decoratorを使って修飾することで、その subsection と aetest に認識されます。
pyATS を実行すると最初に CommonSetup が実行され、その次に Testcase が記述された順番で実行されます。最後に、CommonCleanup が実行されます。
ats.aetest は、各セクション毎に、実行結果をレポートする機能ももっています。
##テストスクリプトの例
Shape class をテストするテストケースを書いてみます。
Testcase classのみを使います。
name が triangle, rectangle, pentagon の Shape object を作って、sides の値が正しいかをcheckします。
pentagon の時には、間違った sides が設定されるので、そのケースは fail になるはずです。
from ats import aetest
from shape import Shape
# test section within Testcases
class Testcase(aetest.Testcase):
# define test section by applying @test decorator
@aetest.test
def testcase_one(self):
shape = Shape('triangle')
assert(shape.sides == 3)
@aetest.test
def testcase_two(self):
shape = Shape('rectangle')
assert(shape.sides == 4)
@aetest.test
def testcase_three(self):
shape = Shape('pentagon')
assert(shape.sides == 5)
##実行方法
前記のテストスクリプトの最後で、ats.aetest.main() を実行すれば、テストを実行できます。今回は、以下のような wrapper を作成して、任意のスクリプトを実行できるようにしました。
#!/usr/bin/env python
from ats.aetest import main
import sys
testscript = sys.argv[1]
main(testable = testscript)
以下のようにして、simple_test.py を実行します。
$ ./run_test.py simple_test.py
##実行結果
結果は、以下のようになります。
testcase_three で fail が発生しています。
2017-12-06T23:53:14: %AETEST-INFO: Starting testcase Testcase
./run_test.py:6: DeprecationWarning: Starting v3.0.0, section.id is deprecated and replaced by section.uid. Please modify your scripts. This will be removed next release
main(testable = testscript)
2017-12-06T23:53:14: %AETEST-INFO: Starting section testcase_one
2017-12-06T23:53:14: %AETEST-INFO: The result of section testcase_one is => PASSED
2017-12-06T23:53:14: %AETEST-INFO: Starting section testcase_two
2017-12-06T23:53:14: %AETEST-INFO: The result of section testcase_two is => PASSED
2017-12-06T23:53:14: %AETEST-INFO: Starting section testcase_three
2017-12-06T23:53:14: %AETEST-WARNING: An assertion failure was caught:
2017-12-06T23:53:14: %AETEST-WARNING: Traceback (most recent call last):
2017-12-06T23:53:14: %AETEST-WARNING: File "/home/tokatsu/pyATS/myCases/simple_case/simple_test.py", line 21, in testcase_three
2017-12-06T23:53:14: %AETEST-WARNING: assert(shape.sides == 5)
2017-12-06T23:53:14: %AETEST-WARNING: AssertionError
2017-12-06T23:53:14: %AETEST-INFO: The result of section testcase_three is => FAILED
2017-12-06T23:53:14: %AETEST-INFO: The result of testcase Testcase is => FAILED
2017-12-06T23:53:14: %AETEST-INFO: +------------------------------------------------------------------------------+
2017-12-06T23:53:14: %AETEST-INFO: | Detailed Results |
2017-12-06T23:53:14: %AETEST-INFO: +------------------------------------------------------------------------------+
2017-12-06T23:53:14: %AETEST-INFO: SECTIONS/TESTCASES RESULT
2017-12-06T23:53:14: %AETEST-INFO: --------------------------------------------------------------------------------
2017-12-06T23:53:14: %AETEST-INFO: .
2017-12-06T23:53:14: %AETEST-INFO: `-- Testcase FAILED
2017-12-06T23:53:14: %AETEST-INFO: |-- testcase_one PASSED
2017-12-06T23:53:14: %AETEST-INFO: |-- testcase_two PASSED
2017-12-06T23:53:14: %AETEST-INFO: `-- testcase_three FAILED
2017-12-06T23:53:14: %AETEST-INFO: +------------------------------------------------------------------------------+
2017-12-06T23:53:14: %AETEST-INFO: | Summary |
2017-12-06T23:53:14: %AETEST-INFO: +------------------------------------------------------------------------------+
2017-12-06T23:53:14: %AETEST-INFO: Number of ABORTED 0
2017-12-06T23:53:14: %AETEST-INFO: Number of BLOCKED 0
2017-12-06T23:53:14: %AETEST-INFO: Number of ERRORED 0
2017-12-06T23:53:14: %AETEST-INFO: Number of FAILED 1
2017-12-06T23:53:14: %AETEST-INFO: Number of PASSED 0
2017-12-06T23:53:14: %AETEST-INFO: Number of PASSX 0
2017-12-06T23:53:14: %AETEST-INFO: Number of SKIPPED 0
2017-12-06T23:53:14: %AETEST-INFO: --------------------------------------------------------------------------------
#Loopを使ってみる
同じようなテストケースを3回書くのは冗長なので、pyATS の loop 機能を使って前記のスクリプトを書き直してみました。
##Loop を使った Testcase
Loop を実行するには、@loop
decorator を使います。@loop
の中で指定した loop parameter を順に使って、loop を実行します。
from ats import aetest
from shape import Shape
# test section within Testcases
class Testcase(aetest.Testcase):
# define test section by applying @test decorator
@aetest.loop(name = ['triangle', 'rectangle', 'pentagon'], sides = [3, 4, 5])
@aetest.test
def testcase_one(self, name, sides):
shape = Shape(name)
assert(shape.sides == sides)
##実行結果
test methond の名前以外は、simple_test.py と同じ結果になります。
method の名前は、元の名前に loop parameter の値を連結したものになります。
2017-12-07T00:00:33: %AETEST-INFO: Starting testcase Testcase
./run_test.py:6: DeprecationWarning: Starting v3.0.0, section.id is deprecated and replaced by section.uid. Please modify your scripts. This will be removed next release
main(testable = testscript)
2017-12-07T00:00:33: %AETEST-INFO: Starting section testcase_one[name=triangle,sides=3]
2017-12-07T00:00:33: %AETEST-INFO: The result of section testcase_one[name=triangle,sides=3] is => PASSED
2017-12-07T00:00:33: %AETEST-INFO: Starting section testcase_one[name=rectangle,sides=4]
2017-12-07T00:00:33: %AETEST-INFO: The result of section testcase_one[name=rectangle,sides=4] is => PASSED
2017-12-07T00:00:33: %AETEST-INFO: Starting section testcase_one[name=pentagon,sides=5]
2017-12-07T00:00:33: %AETEST-WARNING: An assertion failure was caught:
2017-12-07T00:00:33: %AETEST-WARNING: Traceback (most recent call last):
2017-12-07T00:00:33: %AETEST-WARNING: File "/home/tokatsu/pyATS/myCases/simple_case/simple_test_loop.py", line 12, in testcase_one
2017-12-07T00:00:33: %AETEST-WARNING: assert(shape.sides == sides)
2017-12-07T00:00:33: %AETEST-WARNING: AssertionError
2017-12-07T00:00:33: %AETEST-INFO: The result of section testcase_one[name=pentagon,sides=5] is => FAILED
2017-12-07T00:00:33: %AETEST-INFO: The result of testcase Testcase is => FAILED
2017-12-07T00:00:33: %AETEST-INFO: +------------------------------------------------------------------------------+
2017-12-07T00:00:33: %AETEST-INFO: | Detailed Results |
2017-12-07T00:00:33: %AETEST-INFO: +------------------------------------------------------------------------------+
2017-12-07T00:00:33: %AETEST-INFO: SECTIONS/TESTCASES RESULT
2017-12-07T00:00:33: %AETEST-INFO: --------------------------------------------------------------------------------
2017-12-07T00:00:33: %AETEST-INFO: .
2017-12-07T00:00:33: %AETEST-INFO: `-- Testcase FAILED
2017-12-07T00:00:33: %AETEST-INFO: |-- testcase_one[name=triangle,sides=3] PASSED
2017-12-07T00:00:33: %AETEST-INFO: |-- testcase_one[name=rectangle,sides=4] PASSED
2017-12-07T00:00:33: %AETEST-INFO: `-- testcase_one[name=pentagon,sides=5] FAILED
2017-12-07T00:00:33: %AETEST-INFO: +------------------------------------------------------------------------------+
2017-12-07T00:00:33: %AETEST-INFO: | Summary |
2017-12-07T00:00:33: %AETEST-INFO: +------------------------------------------------------------------------------+
2017-12-07T00:00:33: %AETEST-INFO: Number of ABORTED 0
2017-12-07T00:00:33: %AETEST-INFO: Number of BLOCKED 0
2017-12-07T00:00:33: %AETEST-INFO: Number of ERRORED 0
2017-12-07T00:00:33: %AETEST-INFO: Number of FAILED 1
2017-12-07T00:00:33: %AETEST-INFO: Number of PASSED 0
2017-12-07T00:00:33: %AETEST-INFO: Number of PASSX 0
2017-12-07T00:00:33: %AETEST-INFO: Number of SKIPPED 0
2017-12-07T00:00:33: %AETEST-INFO: --------------------------------------------------------------------------------