コンテンツにスキップ

スクリプトを使用して自動テストを行う

Note

ここでは、Python スクリプトを用いて TCP Client を使用した自動テストを行う方法を説明します。

サンプルを使ってスクリプトを理解する

AUTOmeal では Python 形式のスクリプトファイルに自動化したい制御・計測・判定の命令を記述し、そのスクリプトを読み込むことでテストを自動化させることができます。

使用するAPI

サンプルスクリプトで使用される TCP Client 通信の API は以下です。

# クライアント名が "Client1" のクライアントで TCP サーバーへ接続します。
am.connect_tcp_client("Client1")

# クライアント名が "Client1" のクライアントの接続状態を確認します。接続中は True を返します。
am.is_connected_tcp_client("Client1")

# クライアント名が "Client1" のクライアントから、データ [0x01, 0x02, 0x03] を送信します。
am.send_tcp_client("Client1", bytes([0x01, 0x02, 0x03]))

# クライアント名が "Client1" のクライアントで最後に受信したパケットを取得します。
am.get_latest_tcp_client("Client1", "R")

# 指定したクライアントで次のデータを受信するまでタイムアウト付きで待機します。
am.wait_for_next_tcp_client_data("Client1", 5000)

# TCP Client ストリームを開き、受信したデータを順に取得します。
am.create_tcp_client_stream()

# クライアント名が "Client1" のクライアントを TCP サーバーから切断します。
am.disconnect_tcp_client("Client1")

制御では、TCP Client 通信設定で設定した "クライアント名" を指定して接続・送信・切断を操作します。送信データには bytes または bytearray を指定できます。

計測では、クライアント名と送受信方向 "R/S" を指定すると最新パケットを取得できます。受信を待ち合わせる場合は、タイムアウトまでの時間 (ms) も指定します。

ストリームを使用すると、受信パケットをリアルタイムに順次処理できます。create_tcp_client_stream()with 構文と組み合わせて使用します。with ブロックを抜けると自動でストリームが閉じられるため、閉じ忘れを防げます。ストリームが開いている間は for ループで受信パケットを 1 件ずつ取得できます。開始・終了のタイミングを細かく制御したい場合は open_tcp_client_stream() / close_tcp_client_stream() を使用してください。詳細はスクリプトリファレンスを参照してください。

ストリームで取得できる TCPClientRecord オブジェクトには以下のフィールドが含まれます。

フィールド 内容
time タイムスタンプ (timedelta)
direction 送受信方向 ("Receive")
client_name クライアント名
local_ip ローカル IP アドレス
local_port ローカルポート番号
server_ip サーバー IP アドレス
server_port サーバーポート番号
data 送受信データ (bytes)

詳細はスクリプトリファレンスを参照してください。

サンプルスクリプト

クライアント名が "Client1" のクライアントからデータを送信し、TCP サーバーからの応答を受信します。

import automeal as am

# TCP サーバーへ接続します。
am.connect_tcp_client("Client1")

# 接続が確立するまで少し待機します。
am.sleep(0.5)

# 接続状態を確認してから送信します。
if am.is_connected_tcp_client("Client1"):
    # TCP データを送信します。
    am.send_tcp_client("Client1", bytes([0x01, 0x02, 0x03, 0x04]))
else:
    print('未接続のため送信をスキップしました。', flush=True)

# ターゲットからの応答待ちを行います。
am.sleep(0.5)

# 最後に受信したパケットを取得します。
result = am.get_latest_tcp_client("Client1", "R")
if len(result) == 0:
    print('受信データはありません。', flush=True)
else:
    print(f'Client1 受信データ:{result}', flush=True)

# 次のパケットを受信するまで待機 (無限待ち) し、受信したパケットの表示を行います。
result = am.wait_for_next_tcp_client_data("Client1")
print(f'Client1 受信データ:{result}', flush=True)

# TCP Client ストリームを開きます (with を抜けると自動でクローズされます)
# ストリームを使用すると、受信パケットをリアルタイムに順次処理できます。
with am.create_tcp_client_stream() as tcp_stream:
    for data in tcp_stream:
        record = am.TCPClientRecord.from_stream_data(data)
        print(f'クライアント: {record.client_name}', flush=True)
        print(f'サーバー: {record.server_ip}:{record.server_port}', flush=True)
        print(f'受信データ: {record.data.hex()}', flush=True)
        break  # 1 パケット受信したら終了

# TCP サーバーから切断します。
am.disconnect_tcp_client("Client1")

スクリプトをカスタマイズする

スクリプトをカスタマイズしてみましょう。

テキストデータをエンコードして送受信する

テキストデータを送信する場合はエンコードして bytes に変換します。受信したデータはデコードして文字列に戻します。

import automeal as am

# TCP サーバーへ接続します。
am.connect_tcp_client("Client1")
am.sleep(0.5)

# テキストデータを UTF-8 でエンコードして送信します。
am.send_tcp_client("Client1", "Hello TCP".encode("utf-8"))

# 少し待機します。
am.sleep(0.5)

# 最後に受信したパケットを取得します。
result = am.get_latest_tcp_client("Client1", "R")
if len(result) == 0:
    print('受信データはありません。', flush=True)
else:
    # テキストとしてデコードして表示します。
    print(f'Client1 受信データ:{result.decode("utf-8")}', flush=True)

# 次のパケットを受信するまで待機します (タイムアウト 5 秒)。
# 5 秒間受信しなかった場合はエラー (例外) となります。
result = am.wait_for_next_tcp_client_data("Client1", 5000)
print(f'Client1 受信データ:{result.decode("utf-8")}', flush=True)

# TCP サーバーから切断します。
am.disconnect_tcp_client("Client1")

クライアントを絞り込んで処理する

ストリームで受信したパケットのうち、対象のクライアントのものだけを処理します。
client_name を確認し、continue でスキップすることで対象外のパケットを無視できます。

対象が 1 つの場合は != で比較します。

import automeal as am

TARGET_CLIENT = "Client1"

am.connect_tcp_client("Client1")
am.sleep(0.5)

with am.create_tcp_client_stream() as tcp_stream:
    for data in tcp_stream:
        record = am.TCPClientRecord.from_stream_data(data)

        # 対象外のクライアントはスキップします
        if record.client_name != TARGET_CLIENT:
            continue

        # 受信パケットだけ処理します
        if record.direction != "Receive":
            continue

        print(f'受信: {record.server_ip}:{record.server_port} -> {record.data.hex()}', flush=True)
        break  # 1 パケット受信したら終了

am.disconnect_tcp_client("Client1")

複数のクライアントを同時に監視する場合は、セットで管理して not in で絞り込み、client_name で分岐します。

import automeal as am

CLIENTS = {"Client1", "Client2"}
MAX_COUNT = 5  # 受信するパケット数
received = 0

am.connect_tcp_client("Client1")
am.connect_tcp_client("Client2")
am.sleep(0.5)

with am.create_tcp_client_stream() as tcp_stream:
    for data in tcp_stream:
        record = am.TCPClientRecord.from_stream_data(data)

        # 対象外のクライアントはスキップします
        if record.client_name not in CLIENTS:
            continue

        if record.client_name == "Client1":
            print(f'[Client1] {record.data.hex()}', flush=True)
        elif record.client_name == "Client2":
            print(f'[Client2] {record.data.hex()}', flush=True)

        received += 1
        if received >= MAX_COUNT:
            break  # 指定件数に達したら終了

am.disconnect_tcp_client("Client1")
am.disconnect_tcp_client("Client2")

複数パケットをまとめて収集して検証する

指定件数のパケットを収集してから、まとめて内容を検証します。

import automeal as am

MAX_COUNT = 5  # 収集するパケット数
received = []

am.connect_tcp_client("Client1")
am.sleep(0.5)

with am.create_tcp_client_stream() as tcp_stream:
    for data in tcp_stream:
        record = am.TCPClientRecord.from_stream_data(data)
        if record.direction != "Receive":
            continue
        received.append(record)
        if len(received) >= MAX_COUNT:
            break  # 指定件数に達したら終了

# 収集したパケットを検証します
for i, record in enumerate(received):
    print(f'[{i}] {record.server_ip}:{record.server_port} -> {record.data.hex()}', flush=True)

am.disconnect_tcp_client("Client1")

再接続シナリオ

意図的に切断・再接続を行い、サーバーが再起動しても通信が回復することを確認するシナリオです。
TCP は接続状態を持つため、切断後に再接続できるかどうかをテストで検証できます。

import automeal as am

# 1回目の接続・送信
am.connect_tcp_client("Client1")
am.sleep(0.5)
print('1回目の接続完了', flush=True)

am.send_tcp_client("Client1", bytes([0x01]))
am.sleep(0.5)

# 切断します。
am.disconnect_tcp_client("Client1")
print('切断しました。', flush=True)

# サーバーの再起動を待つ時間を模擬します。
am.sleep(1.0)

# 再接続します。
am.connect_tcp_client("Client1")
am.sleep(0.5)

# 再接続後に送信します。
if am.is_connected_tcp_client("Client1"):
    am.send_tcp_client("Client1", bytes([0x02]))
    print('再接続後の送信完了', flush=True)
else:
    print('再接続に失敗しました。', flush=True)

am.disconnect_tcp_client("Client1")

受信内容に応じて他の制御を実行する

ストリームで受信したパケットの内容を判定し、結果に応じてロジック信号などの制御を行うことができます。
以下の例では、特定のコマンドバイトを受信したときにロジック信号を ON にします。

import automeal as am

COMMAND_ON = bytes([0x01])  # ON を意味するコマンド

am.connect_tcp_client("Client1")
am.sleep(0.5)

with am.create_tcp_client_stream() as tcp_stream:
    for data in tcp_stream:
        record = am.TCPClientRecord.from_stream_data(data)

        if record.client_name != "Client1" or record.direction != "Receive":
            continue

        if record.data == COMMAND_ON:
            # コマンドを受信したらロジック信号をONにします
            am.write_logic_rpigp10("Board1", 1, True)
            break

am.disconnect_tcp_client("Client1")

スクリプトを適切な場所に置く

スクリプトは <プロジェクトルート> の中のフォルダに置くことを推奨しています。 スクリプトを配置するフォルダとしては 2 種類あり、その違いは以下です。 自動テストの実行対象とするかどうかで、どちらに配置するか判断してください。

  • <Scripts>
    • 自動テストで実行の対象としたいスクリプトファイル。
  • <Macros>
    • 自動テストで実行の対象としたくないスクリプトファイル。
    • モジュールやライブラリ、特定動作をマクロ化したスクリプトなど、他のスクリプトから呼び出して利用することを想定したスクリプトファイルなどはこちらに配置してください。

スクリプトを TestRunner アプリ GUI 上から実行する

1. テスト実行中の状態で、メニューバーの [テスト] > [スクリプトの実行] をクリックします。

2. Macros または Scripts に配置したスクリプトを選択します。

3. コマンドプロンプトが立ち上がり、スクリプトが実行されるため終了を待ちます。

4. [テストログ] ペインに開始、完了の情報及び、スクリプトに記述したログが出力されます。

スクリプトをコマンドラインから実行する

スクリプトファイルの配置

作成したスクリプトファイルは <プロジェクトルート>\Scripts 以下に配置してください。 フォルダ内にさらにフォルダを作成することもできます。

📂Scripts
 📄FullTest.py
 📂FuncA
  📄FuncATest1.py
  📄FuncATest2.py
  
 📂FuncB
  📄FuncBTest1.py
  📄FuncBTest2.py
 

テストを実行する

プロジェクトルートでコマンドプロンプトを起動し、コマンドを実行します。

パターンの指定は <プロジェクトルート>\Scripts からの相対パスで指定します。

例 :

  • 全てのスクリプトを実行する

    TestRunnerCLI 
    
  • 特定のスクリプトのみ実行する

    TestRunnerCLI FuncA\FuncATest1.py
    
  • 特定のフォルダ以下すべてを実行する

    TestRunnerCLI FuncB\*.py
    
  • 特定のファイル名を含むスクリプトを実行する

    TestRunnerCLI **\Func*Test*.py
    

Note

一度の実行で実行されるスクリプトは順不同になります。 実行順を担保したい場合はコマンドを複数回に分けて実行してください。

実行中

実行中はコマンドプロンプトに実行ログと進捗の表示が行われます。

実行完了すると、次のコマンドが実行できるようになります。

結果を確認する

TestRunner アプリ上でのログの確認

アプリケーション下部の [出力] ペインには、動作ログやエラー詳細情報などが表示されます。 [テストログ] ペインにはテストログやテスト実行中のエラー情報などが表示されます。 テストスクリプトに print 文で記述した内容は [テストログ] ペインに表示されます。

種別 文字色 内容
Error 赤系 期待する動作の完了・継続に支障が出た場合の通知です。
Warn 黄系 何らかの不都合が生じたものの、処理の完了・継続に支障がない場合の通知です。
Info 黒系 動作開始・完了などユーザがアプリの状態を把握するための情報です。スクリプトで print を行った場合 Info レベルで出力されます。

全体サマリーの確認

Note

全体サマリーは TestRunnerCLI (コマンドライン) から自動テストを実行した場合のみ生成されます。

<プロジェクトルート>\Reports にあるテストランフォルダを確認します。

確認したい実行日時のテストランフォルダを開いてください。

テキストエディタや Excel 等でテストランフォルダ内にある Result.csv を開きます。

Result.csv には実行結果、実行日時、実行したスクリプト名が記載されています。

CSV 形式のため、必要に応じてツール等で結果を集計できます。