Edited at

pyATSの初歩

More than 1 year has passed since last update.


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


テストをしてみる


テスト対象

テスト対象として以下のようなクラスを使いました。


shape.py

#!/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 になるはずです。


simple_test.py

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 を作成して、任意のスクリプトを実行できるようにしました。


run_test.py

#!/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 を実行します。


simple_test_loop.py

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: --------------------------------------------------------------------------------