はじめに
こちらの記事は「Selenium/Appium Advent Calendar 2019」13日分の投稿です。
@kazurasaka がお届けします。
あらすじ
それは数ヶ月前のことだった。転職した先で与えられたのはQAエンジニアの肩書。自社アプリのテスト自動化研究の始まりが訪れ、QA界隈に突如飛び込んだ私は色々と勉強することになる。
課せられた制約は2個
- 実際に動いているsourceをテストのために変更することはできない
- 複数タイトルに流用できるテストを構築しなくてはならない
IDで検索する方法は使えない……そんななか一つの記事が目に留まる。
画像による要素検索
http://www.selenium.jp/translation/huaxiangniyoruyaosujiansuopart1
こ、これだーーーー!!
本題
では見ていきましょう。
おもむろにgithubでappium縛りで「findElementByImage」と検索をかけます。
https://github.com/search?q=org%3Aappium+findElementByImage&type=Code
なんとなく関係ありそうなコードってjavaかよってなりながら読みます(javaまともにやったことないからフィーリングで切り抜けます)
https://github.com/appium/java-client/blob/14786471f4abb85a01dfe79879d7a617cd2fc669/src/main/java/io/appium/java_client/FindsByImage.java
default T findElementByImage(String b64Template) {
return findElement(MobileSelector.IMAGE.toString(), b64Template);
}
次は findElement
ね。ふむふむ。
これは探すとjsの実装を見つけられました。
https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/commands/find.js
commands.findElement = async function findElement (strategy, selector) {
if (strategy === IMAGE_STRATEGY) {
return await this.findByImage(selector, {multiple: false});
} else if (strategy === CUSTOM_STRATEGY) {
return await this.findByCustom(selector, false);
}
return await this.findElOrElsWithProcessing(strategy, selector, false);
};
custom
も気になりましたが findByImage
のほうがそれっぽいので追いかけます。
長いので一部抜粋
https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/commands/find.js#L204
const comparedImage = await this.compareImages(MATCH_TEMPLATE_MODE, b64Screenshot, b64Template, {threshold, visualize});
なんでこの行に目をつけたかって?引数の内容からです。
- screenshot
- template
- threshold
がありますね?
- screenshot:ベースの画像。探す対象。
- template:テンプレート画像。この画像をscreenshotから探す。
- threshold:閾値
です。
ここらへんは読む前にopenCVの画像認識を触っていたのでなんとなくわかりました。
次、 comparedImage
を探します。
https://github.com/appium/appium-base-driver/blob/5b12eebcf011cf7676a2987b646a6394ddafb639/lib/basedriver/commands/images.js#L34
case MATCH_TEMPLATE_MODE.toLowerCase():
// firstImage/img1 is the full image and secondImage/img2 is the partial one
result = await imageUtil.getImageOccurrence(img1, img2, options);
break;
TEMPLATE_MODE
が引数なのでこの行ですね。
getImageOccurrence
を探します。
const matched = await fullImg.matchTemplateAsync(partialImg, cv.TM_CCOEFF_NORMED);
たどり着いたーーーー!ここの実装が肝!なぜなら matchTemplate
って openCV
のやつだから。
このまま matchTemplateAsync
も辿ってみるのもありですが、それは opencv4node.js
さんの領分なので今回はここまでです。
ざっと読んでみたところ、 matchTemplate
つまりはテンプレートマッチングで対象から検索していますね。なるほど。
なるほど?
オチ
appium + webdriverio + mocha でやってるんですけどfindElementByImageがwebdriverioになかったね!!!!
普通にopencv4node.js直接使って構築しましたとさ。
更に小ネタ
テンプレートマッチングだとやりたい画像ではmatch率イマイチだったので特徴点抽出つかって識別しました。