gaugeとTypeScriptでテストをいい感じにする
はじめに
- 今回はgaugeというテストライブラリを使い、web driverを使ったテストを書いていこうと思います
そもそもgaugeとは
- マークダウン形式でテストシナリオを定義でき、実行ファイルとシナリオを定義しているファイルを分離できるというもの
- メリットとしては、テストを自然言語でかつマークダウンで書けることで、単にテストを書くよりも可読性が大幅に上がるという点があります。
はじめる
- OSX前提で書きます
- ターミナルにHomebrewが入ってる前提で進めます
- nodeとnpmも入ってる前提で進めます
gaugeをインストールする
$ brew install gauge # gaugeのバージョンが見られればOK $ gauge -v Gauge version: 1.0.9 Plugins ------- html-report (4.0.8) java (0.7.3) screenshot (0.0.1) ts (0.1.0) xml-report (0.2.2)
- VSCode のExtensionがあるのでそれを入れておく
- 任意のディレクトリでgaugeプロジェクトを作成する
# 用意されているテンプレートを確認しておく $ gauge init -t dotnet java java_gradle java_maven java_maven_selenium js js_simple python python_selenium ruby ruby_selenium ts csharp # 今回はtsで作成する $ gauge init ts Downloading ts.zip Copying Gauge template ts to current directory ... > protobufjs@6.10.1 postinstall /Users/yuta.aikawa/sources/gauge/sample/node_modules/protobufjs > node scripts/postinstall npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN gauge-ts-template@0.0.1 No repository field. npm WARN gauge-ts-template@0.0.1 No license field. added 88 packages from 134 contributors and audited 88 packages in 3.763s 3 packages are looking for funding run `npm fund` for details found 0 vulnerabilities Successfully initialized the project. Run specifications with "gauge run specs/".
. ├── env ├── logs ├── manifest.json ├── node_modules ├── package-lock.json ├── package.json ├── reports ├── specs ├── tests └── tsconfig.json
- 今回用があるのは、
spec
とtests
のみなので、まずは、spec
から見ていく。 - gaugeの世界には、scenario/ suite/ stepなど色々粒度の異なる概念が登場してくるが、そこら辺の説明は公式に任せる
テンプレートで作成した、spec以下には spec > scenario > stepという感じで定義されていて、雑に言えばこの粒度で定義していけば良い。
次に
test
側を見ていく。- test以下には既に、tsファイルが用意されていて、ファンクションに
@step("hogehoge")
みたいな物がついている - そのアノテーションの中で定義している文字をspecファイル側から呼び出すという簡単な話である
実行する
- 概要はざっくり説明したので、あとは動くか確認する
$ gauge run # Specification Heading ## Vowel counts in single word ✔ ✔ ## Vowel counts in multiple word ✔ ✔ Successfully generated html-report to => /Users/yuta.aikawa/sources/gauge/sample/reports/html-report/index.html Specifications: 1 executed 1 passed 0 failed 0 skipped Scenarios: 2 executed 2 passed 0 failed 0 skipped Total time taken: 285ms Updates are available. Run `gauge update -c` for more info.
- こんな感じで、動けばOK
selenium側
- webdriverを用意する
$ brew tap homebrew/cask $ brew cask install chromedriver $ chromedriver -v $ ChromeDriver 85.0.4183.87 (cd6713ebf92fa1cacc0f1a598df280093af0c5d7-refs/branch-heads/4183@{#1689})
- 先ほど作成したgaugeプロジェクトで
selenium webdriver
を使えるようにする
$ npm install -D typescript selenium-webdriver @types/selenium-webdriver
- 実装していく
- まずは、specファイル側から定義していく
# Specification Heading ## Sportsnaviにアクセスする * sportsnaviにアクセスする
- この状態では対応する実装がないので怒られると思うので、ts側の実装をしていく。
import { Step } from "gauge-ts"; import { Builder, Capabilities, WebDriver } from 'selenium-webdriver' export default class Access { @Step("sportsnaviにアクセスする") public async accessSportsNaviTopPage() { await access("https://sports.yahoo.co.jp/") } } # SeleniumWebDriverが用意してるインターフェースを呼び出してDriverの設定を書いておく const capabilities: Capabilities = Capabilities.chrome() capabilities.set('chromeOptions', { args: [ '--disable-gpu', '--window-size=1024,768' ], w3c: false }) # WebDriverを呼び出して受け取ったURLにアクセスする async function access(url: string): Promise<void> { const driver: WebDriver = await new Builder() .withCapabilities(capabilities) .build() try { await driver.get(url) } finally { driver && await driver.quit() } }
- これで実行する
$ gauge run gauge run # Specification Heading ## Sportsnaviにアクセスする ✔ Successfully generated html-report to => /Users/yuta.aikawa/sources/gauge/e2e/reports/html-report/index.html Specifications: 1 executed 1 passed 0 failed 0 skipped Scenarios: 1 executed 1 passed 0 failed 0 skipped Total time taken: 2.859s Updates are available. Run `gauge update -c` for more info.
- Chromeが立ち上がり、上記な感じで結果が出力される。
ここまでのまとめ
- setup自体はめちゃくちゃ簡単
- ただし、seleniumのインターフェースを呼び出して、直接諸々操作しないといけないので、selenideに比べるとキャッチアップにコストがかかりそう。 Selenide: concise UI tests in Java
puppeteerを使ってみる
- もうちょっと、seleniumとかdriverを意識せずに使いたいので、puppeteerを試してみる github.com
$ npm install -D typescript puppeteer @types/puppeteer
- 実装側の修正をする
import { Step } from "gauge-ts"; import { launch } from 'puppeteer' export default class StepImplementation { @Step("sportsnaviにアクセスする") public async accessSportsNaviTopPage() { await access("https://sports.yahoo.co.jp/") } } async function access(url: string): Promise<void> { const browser = await launch( { headless: false } ); const page = await browser.newPage(); await page.goto(url) await browser.close(); }
- とりあえず、chroniumが動いてサイトまで飛ぶことはできた。
- もう一歩踏み込んでselectorを指定して取得する
async function access(url: string): Promise<void> { const browser = await launch( { headless: false } ); const page = await browser.newPage(); await page.goto(url) # pageに対してjqueryっぽく指定して要素を取得することができる const headersCount = await page.$$eval('#h_nav .clearfix > li', headers => headers.length); console.log(headersCount) await browser.close(); }
- もちろん
getElementById
みたいな感じでJSから取得することも可能 - ただし、selenideでできていたアサーションに関しては、puppeteerにはないので、jestあたりを使う必要がありそう(expect-pupperteerというのがあるらしい) github.com
まとめ
- 元々はVSCodeで書ける言語ということで、Java/KotlinからTSへの移行を考えて始めた調査でした。
- 実際にやってみると、selenideに慣れていたせいもあり、selenideから素のseleniumもしくはpuppeteerへの移行はちょっとハードルが高いなという印象が残った
- 特に、selenideのアサーション周りで結構楽に実装できていた部分をpuppeteerを使うとしたら頑張る必要がありそう
- ということで、最終的な結論として、個人的にはselenideに慣れてる人にとってはgaugeの為だけに言語を乗り換えるというのはあまりオススメできない
参考
- 作りかけのコードはあげときます https://github.com/yuaikawa/e2e