※ 結論だけ知りたい方は、4.結論 をご覧ください 🙏
1.前提とこの記事で解決したい課題
1-1.前提
最近は MagicPod という E2E テスト自動化ツールを触ることが多い。
MagicPod では、複数のテストシナリオを実行(MagicPod ではこれを テスト一括実行/一括テスト実行 と称している。)することができる。また、テスト対象のアプリケーションのアーカイブ(App ファイル)には、MagicPod 上でユニークな連番である、app_file_number
を割り当てられ、テスト一括実行
の際にはこの app_file_number
を指定することで、任意の App ファイルを指定することもできる。
app_file_number
のいちばんカンタンな取得方法は、App ファイルを MagicPod 側にアップロードする際のレスポンスから取得することである。
$ app_file_number=$(./magicpod-api-client upload-app -a <path to app/ipa/apk>)
$ ./magicpod-api-client batch-run \
-t "${token}" -o "${organization}" -p "${project}" -S "${test_settings_number}" \
-s "{\"app_file_number\":\"${app_file_number}\"}"
1-2.この記事で解決したい課題
しかし、App ファイルを MagicPod 上にアップロードするジョブとテスト一括実行のジョブが独立しているとしたらどうだろうか。App ファイルのアップロードのレスポンスから取得した app_file_number
をテスト一括実行の引数として渡すことができない。そのため、指定したい App ファイルの app_file_number
の取得方法を新たに用意しなければならない。
あるいは、App ファイルに test
など任意の文字列が含まれさえしなければ、最新のもの指定したい場合もあるだろう。この場合はどうやって指定すればよいだろうか。
この記事では、そのような課題に対する解決策のひとつを書き留めたい。
2.環境情報
- この記事で扱う MagicPod Web API のバージョンは v1.0
3.MagicPod のテスト一括実行をおこなう際、任意の App ファイルの app_file_number を指定する
3-1.MagicPod のテスト一括実行をおこなう際、直近にアップロードした App ファイルを指定する
MagicPod Web API を眺めると、/v1.0</{organization_name}/{project_name}/list-files
API で、以下のように書かれていることに目がいく。
Get list of app files (.app, .ipa, .apk or .aab) in MagicPod cloud. If no app files uploaded,
app_files parameter in response will have an empty array
.
さっそく、app_files
配列の中身がどういう構造となっているのか? /v1.0</{organization_name}/{project_name}/list-files
API を叩いてみよう。すると、以下のようなレスポンスが返ってくる。
$ curl -sS -X GET https://app.magicpod.com/api/v1.0/${ORGANIZATION}/${PROJECT}/list-files/ \
> -H "Authorization: Token ${TOKEN}" | \
> jq -r
{
"app_files": [
{
"app_file_name": "<xxx1>",
"app_file_number": 333(数字は書き換えている。以降では書き換えている旨は記載しない。),
"app_file_created": <0000(アップロードされた日時のタイムスタンプと推測)>
},
{
"app_file_name": "<xxx2>",
"app_file_number": 332,
"app_file_created": <0000(アップロードされた日時のタイムスタンプと推測)>
略
上記のレスポンスの app_files 配列内の、app_file_number
と app_file_name
の並びから、最新の App ファイルは最後尾にあると考えてよさそうだ。実際、0 番目(先頭)に位置する app_file_number
は、テストケースを実行する際「最後にアップロードしたファイル」を選択した場合に使われる App ファイルの隣に記載された数字と一致していた。
このことから、直近にアップロードされた App ファイルを指定するには、app_files
配列の 0 番目(先頭)に位置する app_file_number
を指定すればよいだろう。
app_files
配列の 0 番目(先頭)に位置する app_file_number
を指定する方法はいくつかあると思うが、jq ではこのように書くことができる。
$ curl -sS -X GET https://app.magicpod.com/api/v1.0/${ORGANIZATION}/${PROJECT}/list-files/ \
> -H "Authorization: Token ${TOKEN}" | jq -r '.app_files[0].app_file_number'
333
ちなみに、直近にアップロードされた App ファイルを指定するだけでよければ、わざわざ上記の方法ではなくても、app_file_number を渡さず CrossBatchRun
API を POST するだけでもできる。
※ CrossBatchRun
API と似たようなものとして、BatchRun
API があるのだが、こちらは非推奨とのこと。
参考: /org/proj/batch-run の API を画面上で非推奨にする · Issue #588 · Magic-Pod/japanese-issue-and-doc
ところで、数ある App ファイルの指定方法のうち、最初に、app_files
配列の 0 番目(先頭)に位置する app_file_number
を指定する方法をとりあげた理由は、以下の 2 点である。
- やりかたがカンタンそうだから。
- またやりかたがカンタンそうであるがために、いちはやく任意の App ファイルを指定するために使う
/v1.0</{organization_name}/{project_name}/list-files
API を叩いたときのレスポンスがどんなものか?おさえておくことができるだろうと考えたため。
3-2.MagicPod のテスト一括実行をおこなう際、任意の文字列を含む App ファイルを指定する
※ 例として、something
という任意の文字列を含む app_file_name
のうち、最後にアップロードされた app_file_name
と同じ配列の app_file_number
を指定する。
jq を使うと以下の 2 つの書き方ができる。
- select 関数と contains 関数を使う
select(contains({app_file_name: "'${FILTER}'"}))
The filter contains(b) will produce true
if b is completely contained within the input
.- 参考: https://stedolan.github.io/jq/manual/#contains(element)
- select 関数と test 関数を使う
select(.app_file_name | test(".*'${FILTER}'.*"))
Like match, but does not return match objects, only true or false for whether or not
the regex matches the input
.- 参考: https://stedolan.github.io/jq/manual/#test(val),test(regex;flags)
両者の類似点と相違点
- 類似点は、どちらも検索結果に応じて boolean 値を返却すること。
- 相違点は、検索方法にある。前者は文字列があるかないかの単純な比較であり、後者は正規表現も使えること。
具体的にはこのように書く。
select 関数と contains 関数を使った場合
$ FILTER=something
$ curl -sS -X GET https://app.magicpod.com/api/v1.0/${ORGANIZATION}/${PROJECT}/list-files/
> -H "Authorization: Token ${TOKEN}" | \
> jq -r '.app_files[] | select(contains({app_file_name: "'${FILTER}'"}))'
333
332
略
select 関数と test 関数を使った場合
$ curl -sS -X GET https://app.magicpod.com/api/v1.0/${ORGANIZATION}/${PROJECT}/list-files/ \
> -H "Authorization: Token ${TOKEN}" | \
> jq -r '.app_files[] | select(.app_file_name | test(".*'${FILTER}'.*")) | .app_file_number'
333
332
略
あとは、app_file_number
郡を配列として受け取って、0 番目を指定すれば ok
$ curl -sS -X GET https://app.magicpod.com/api/v1.0/${ORGANIZATION}/${PROJECT}/list-files/ \
> -H "Authorization: Token ${TOKEN}" | \
> jq -r '[.app_files[] | select(.app_file_name | test(".*'${FILTER}'.*")) | .app_file_number][0]'
333
参考: https://stedolan.github.io/jq/manual/#test(val),test(regex;flags)
test 関数に渡す変数は正規表現も受け付ける! すごい!!
$ cat dummy.json
{
"app_files": [
{
"app_file_name": "xxx-app-147",
"app_file_number": 330,
"app_file_created": 1234567891
}
{
"app_file_name": "xxx-app-139",
"app_file_number": 329,
"app_file_created": 1234567891
}
{
"app_file_name": "dummy-xxx-app-139",
"app_file_number": 329,
"app_file_created": 1234567891
}
}
$ FILTER=^xxx-app-1[3-4]9
$ cat dummy.json | \
> jq -r '.app_files[] | select(.app_file_name | test("'${FILTER}'"))'
{
"app_file_name": "xxx-app-139",
"app_file_number": 329,
"app_file_created": 1234567891
}
$ FILTER=^xxx-app-13[79]
$ cat dummy.json | \
> jq -r '.app_files[] | select(.app_file_name | test("'${FILTER}'"))'
{
"app_file_name": "xxx-app-139",
"app_file_number": 329,
"app_file_created": 1234567891
}
3-3.MagicPod のテスト一括実行をおこなう際、任意の文字列を含まない App ファイルを指定する
今度は逆に任意の文字列を含まない App ファイルを指定してみよう。
※ 例として、foo
という任意の文字列を含まない app_file_name
のうち、最後にアップロードされた app_file_name
と同じ配列の app_file_number
を指定する。
書き方としては、3-2.MagicPod のテスト一括実行をおこなう際、任意の文字列を含む App ファイルを指定する で紹介した方法に、not
で boolean 値を反転させるだけである。
select 関数と contains 関数を使った場合
$ IGNORED=foo
$ curl -sS -X GET https://app.magicpod.com/api/v1.0/${ORGANIZATION}/${PROJECT}/list-files/ \
> -H "Authorization: Token ${TOKEN}" | \
> jq -r '[.app_files[] | select(contains({app_file_name: "${IGNORED}"})|not) | .app_file_number][0]'
99
select 関数と test 関数を使った場合
$ IGNORED=foo
$ curl -sS -X GET https://app.magicpod.com/api/v1.0/${ORGANIZATION}/${PROJECT}/list-files/ \
> -H "Authorization: Token ${TOKEN}" | \
> jq -r '[.app_files[] | select(.app_file_name | test("'${IGNORED}'")|not) | .app_file_number][0]'
99
not
を使う場合も test 関数に渡す値は正規表現も受け付けることがわかった!
$ IGNORED=^xxx-app-13[79]
$ cat dummy.json | \
> jq -r '.app_files[] | select(.app_file_name | test("'${IGNORED}'")|not)'
{
"app_file_name": "xxx-app-147",
"app_file_number": 330,
"app_file_created": 1234567891
}
{
"app_file_name": "dummy-xxx-app-139",
"app_file_number": 329,
"app_file_created": 1234567891
}
4.結論
任意の App ファイルの指定方法を、IGNORED
という変数を含まず、かつ FILTER
という変数を含む app_file_name
の app_files
の配列から 0 番目の app_file_number
を指定することとした場合、以下のように jq の test 関数を使って書くのがよいだろう。
contains 関数ではなく test 関数を推す理由は、上述したとおり正規表現が使えるためである。
$ IGNORED=.*foo.*
$ FILTER=^xxx-app-13[0-9]
$ curl -sS -X GET https://app.magicpod.com/api/v1.0/${ORGANIZATION}/${PROJECT}/list-files/ \
> -H "Authorization: Token ${TOKEN}" | \
> jq -r '[.app_files[] | select(.app_file_name | test("'${IGNORED}'")|not) | select(.app_file_name | test("'${FILTER}'"))][0]'
5.小ネタ
5-1.指定する配列のインデックスの変数化
これまではレスポンスを jq で該当の配列のインデックを 0 番目などとベタで指定していたが、実はこれも変数として扱うことができる。
以下のように --argjson
オプションを渡すだけだ。
$ number=0
$ cat dummy.json | \
> jq -r '[.app_files[] | select(.app_file_name | test("'${FILTER}'")) | select(.app_file_name | test("'${IGNORED}'")|not)]' | jq -r --argjson index ${number} '.[$index]'
{
"app_file_name": "xxx-app-147",
"app_file_number": 330,
"app_file_created": 1234567891
}
app_file_number
だけ取得したい場合、このように書ける。
$ number=0
$ cat dummy.json | \
> jq -r '[.app_files[] | select(.app_file_name | test("'${IGNORED}'")|not)]' | jq -r --argjson index ${number} '.[$index].app_file_number'
330
参考: json - How I pass argument as index to jq? - Stack Overflow
5-2.テスト一括実行時に変数を渡す
この記事のテーマとずれるが、使いがちなので紹介したい。
テスト一括実行時に変数を渡すには、shared_variables
を使う。shared_variables
には、key
value
secret
の 3 つのパラメータがある。secret
とは、ログで value
をマスク化するかしないか boolean 値で指定するパラメータである。
一例として、Magic-Pod/magicpod-api-client を使った場合、このように書く。
$ YOUR_VARIABLE=hoge
$ ./magicpod-api-client batch-run \
-t "${TOKEN}" -o "${ORGANIZATION}" -p "${PROJECT}" \
> -S "${TEST_SETTINGS_NUMBER}" \
-s "{\"app_file_number\":\"${APP_FILE_NUMBER}\", \"shared_variables\": [{\"key\":\"YOUR_VARIABLE\",\"value\":\"${YOUR_VARIABLE}\",\"secret\":false}]}"
MagicPod のテストケース側で渡した変数の値を参照するには、参照したいテストケースのコマンドで ${YOUR_VARIABLE}
と書けばよい。
6.参考
- MagicPod Web API
- MagicPod のテスト一括実行/一括テスト実行
- 変数の活用 – MagicPod ヘルプセンター
- /org/proj/batch-run の API を画面上で非推奨にする · Issue #588 · Magic-Pod/japanese-issue-and-doc
- jq コマンドのドキュメント