selenium webdriver使ってみようず
TRANSCRIPT
わんくま同盟 大阪勉強会 #48
SeleniumWebDriver使ってみようず
2012/04/07 お だ
わんくま同盟 大阪勉強会 #48
自己紹介
• 織田 信亮 ( おだ しんすけ )• 大阪で開発者しています• SQLWorld 代表 (http://sqlworld.org)
• http://d.hatena.ne.jp/odashinsuke/• Twitter:@shinsukeoda
わんくま同盟 大阪勉強会 #48
~基本~
わんくま同盟 大阪勉強会 #48
Selenium ってなに?
• Web アプリケーション用のテストツール• ブラウザを使って Web アプリケーショ
ンの動作確認等を行う• ブラウザの操作を Selenium が行ってく
れる
わんくま同盟 大阪勉強会 #48
Selenium WebDriver ってなに?
http://seleniumhq.org/docs/03_webdriver.html• Selenium が WebDriver と統合された• Selenium 1.0 だと JavaScript/HTML で
記述がメイン• WebDriver は、 Selenium ではセキュリ
ティで制限されていたものが回避出来る• Selenium 2.0 で統合!
わんくま同盟 大阪勉強会 #48
API が提供されている言語
• Java• C#• Python• Ruby• PHP• Perl
わんくま同盟 大阪勉強会 #48
提供されている WebDriver
• HtmlUnit Driver• Firefox Driver• Internet Explorer Driver• Chrome Driver• Opera Driver• iPhone Driver• Android Driver
言語によっては、提供されていない Driver もある!
わんくま同盟 大阪勉強会 #48
API の基本
• ドライバー– IWebDriver (WebDriver)
• エレメント– IWebElement(WebElement)
• ロケーター– By
わんくま同盟 大阪勉強会 #48
using System; using OpenQA.Selenium.IE; using OpenQA.Selenium;
class Program { static void Main(string[] args) { IWebDriver driver = new InternetExplorerDriver(); Console.ReadKey(); driver.Quit(); } }
とりあえず、ブラウザ起動してみる
わんくま同盟 大阪勉強会 #48
ドライバー の API
• ページのタイトル取得• 要素の検索
– ISearchContext を実装• FindElement, FindElements
• ページ遷移– INavigation を保持
• GoToUrl, Back, Forward
• コンテキストの切り替え– ITargetLocator を保持
• Alert, Frame, Window
わんくま同盟 大阪勉強会 #48
using System; using OpenQA.Selenium.IE; using OpenQA.Selenium;
class Program { static void Main(string[] args) { IWebDriver driver = new InternetExplorerDriver(); driver.Navigate().GoToUrl("http://www.bing.com"); Console.WriteLine(driver.Title); Console.ReadKey(); driver.Quit(); } }
Bing にいってみる
わんくま同盟 大阪勉強会 #48
using System; using OpenQA.Selenium.IE; using OpenQA.Selenium;
class Program { static void Main(string[] args) { IWebDriver driver = new InternetExplorerDriver(); driver.Navigate().GoToUrl("http://www.bing.com"); IWebElement element = driver.FindElement(By.Name("q")); element.SendKeys(" セレニウム ウェブドライバー "); Console.ReadKey(); driver.Quit(); } }
テキストボックスに文字を入力
わんくま同盟 大阪勉強会 #48
わんくま同盟 大阪勉強会 #48
ロケーターには何がある?
• Id• Name• TagName• ClassName• CssSelector• LinkText• PartialLinkText• XPath
わんくま同盟 大阪勉強会 #48
using System; using OpenQA.Selenium.IE; using OpenQA.Selenium;
class Program { static void Main(string[] args) { IWebDriver driver = new InternetExplorerDriver(); driver.Navigate().GoToUrl("http://www.bing.com"); IWebElement elementByName = driver.FindElement(By.Name("q")); elementByName.SendKeys(" セレニウム ウェブドライバー "); IWebElement elementById = driver.FindElement(By.Id("sb_form_q")); elementById.SendKeys(" ID で取ったお "); IWebElement elementByCss = driver.FindElement( By.CssSelector("input.sw_qbox")); elementByCss.Clear(); elementByCss.SendKeys("CssSelector で "); Console.ReadKey(); driver.Quit(); }}
色んな取り方をしてみる
わんくま同盟 大阪勉強会 #48
エレメント の メソッド
• SendKeys• Clear• Click• GetAttribute
– input タグの入力値はこれで取得する • GetCssValue• Submit• 要素の検索 (ISearchContext を実装 )
わんくま同盟 大阪勉強会 #48
エレメント の プロパティ
基本 get だけ• Displayed• Enabled• Location• Selected
– チェックボックスや Select の Option• Size• TagName• Text
– タグに挟まれたテキストのこと– テキストボックスの値じゃない!
わんくま同盟 大阪勉強会 #48
~実践~
わんくま同盟 大阪勉強会 #48
みんな大好き IE Driver を使う
• 前準備– IE がインストールされている– ツール => インターネット オプション =>
セキュリティ タブ => 全てのゾーンで「保護モードを有効にする」チェック値を統一
わんくま同盟 大阪勉強会 #48
Chrome Driver を使う
• 前準備– Chrome がインストールされている– ChromeDriver.exe をダウンロードするhttp://code.google.com/p/chromedriver/downloads/list
わんくま同盟 大阪勉強会 #48
C# で
• Visual Web Developer 2010 Express (VWD2010)
• 拡張機能マネージャーから NuGet インストール
• Package Manage Console(NuGet) から
– Support は便利だからいれてます
Install-Package Selenium.WebDriverInstall-Package Selenium.Support
ここでは、以下のライブラリも利用していますNunit 、 ChainingAssertion.NUnit
わんくま同盟 大阪勉強会 #48
[Test]public void 検索 () { var driver = new InternetExplorerDriver(); try { driver.Navigate().GoToUrl("http://www.bing.com"); var txt 条件 = driver.FindElementByName("q"); txt 条件 .SendKeys("Microsoft"); txt 条件 .Submit(); Thread.Sleep(3000); // 次の画面に遷移するまで待つ var lbl 件数 = driver.FindElementById("count"); Regex.Match(lbl 件数 .Text, "(?<=of ).*(?= results)").Value.Is("527,000,000"); } finally { driver.Quit(); }}
わんくま同盟 大阪勉強会 #48
便利なやつ その 1
• IWait(Wait)– Selenium.Support に含まれている
• OpenQA.Selenium.Support.UI.IWait
– Thread.Sleep はもう古い!– 指定条件を満たすまで待機する– タイムアウト指定することで、異常時には例
外で終了する
わんくま同盟 大阪勉強会 #48
Wait
• WebDriverWait が良く使われる• コンストラクタでタイムアウト時間指定• Until メソッドに Func を渡し条件指定• Func は、既定の実装が幾つかある
– ExpectedConditions
• Func を自前で実装も可wait.Until(d => d.Title == "Microsoft - Bing");
IWait<IWebDriver> wait = new WebDriverWait(driver, new TimeSpan(0, 0,10));wait.Until(ExpectedConditions.TitleIs("Microsoft - Bing"));
わんくま同盟 大阪勉強会 #48
[Test]public void 検索 () { var driver = new InternetExplorerDriver(); try { driver.Navigate().GoToUrl("http://www.bing.com"); var txt 条件 = driver.FindElementByName("q"); txt 条件 .SendKeys("Microsoft"); txt 条件 .Submit(); var wait = new WebDriverWait(driver, new TimeSpan(0, 0, 10)); wait.Until(ExpectedConditions.TitleIs("Microsoft - Bing")); var lbl 件数 = driver.FindElementById("count"); Regex.Match(lbl 件数 .Text, "(?<=of ).*(?= results)").Value.Is("527,000,000"); } finally { driver.Quit(); }}
わんくま同盟 大阪勉強会 #48
PageObject パターン
• 一つの HTML の操作は一つの場所で• 一つのページを一つのオブジェクトとし
て扱い、カプセル化• メソッドは実行する機能を表現する
(DOM の操作ではない )http://memolog.org/2010/11/page_objects.php
http://code.google.com/p/selenium/wiki/PageObjects
わんくま同盟 大阪勉強会 #48
[TestFixture]public class カプセル化 { [Test] public void 検索 () { var driver = new InternetExplorerDriver(); try { var instance = BingSearch.Create(driver); instance.Input 検索条件 ("Microsoft"); instance.Submit 検索 (); instance.Get 検索結果件数 ().Is("527,000,000"); } finally { driver.Quit(); } }}
わんくま同盟 大阪勉強会 #48
public class BingSearch { public static BingSearch Create(RemoteWebDriver driver) { var instance = new BingSearch(driver); driver.Url = "http://www.bing.com"; return instance; } private readonly RemoteWebDriver driver; private BingSearch(RemoteWebDriver driver) { this.driver = driver; } private IWebElement txt 条件 { get { return driver.FindElementByName("q"); } } private IWebElement lbl 件数 { get { return driver.FindElementById("count"); } }
わんくま同盟 大阪勉強会 #48
public void Input 検索条件 (string 条件 ) { this.txt 条件 .Clear(); this.txt 条件 .SendKeys( 条件 ); } public void Submit 検索 () { this.txt 条件 .Submit(); var wait = new WebDriverWait(this.driver, new TimeSpan(0, 0, 10)); wait.Until(ExpectedConditions.TitleIs( "Microsoft - Bing")); } public string Get 検索結果件数 () { return Regex.Match(this.lbl 件数 .Text, "(?<=of ).*(?= results)").Value; }}
わんくま同盟 大阪勉強会 #48
Java (Maven) で
• selenium-java
• ie-driver や support もついてくる
<dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>2.20.0</version></dependency>
わんくま同盟 大阪勉強会 #48
@Testpublic void 検索 () { RemoteWebDriver driver = new InternetExplorerDriver(); try { driver.navigate().to("http://www.bing.com"); WebElement txt 条件 = driver.findElementByName("q"); txt 条件 .sendKeys("Microsoft"); txt 条件 .submit(); Wait<WebDriver> wait = new WebDriverWait(driver, 10); wait.until(titleIs("Microsoft - Bing")); WebElement lbl 件数 = driver.findElementById("count"); assertThat(lbl 件数 .getText().replaceAll( "(.*of )(.*)( results)", "$2"), is("527,000,000")); } finally { driver.quit(); }}
わんくま同盟 大阪勉強会 #48
@Testpublic void カプセル化 () { RemoteWebDriver driver = new InternetExplorerDriver(); try { BingSearch instance = BingSearch.create(driver); instance.input 検索条件 ("Microsoft"); instance.submit 検索 (); assertThat(instance.get 検索結果件数 (), is("527,000,000")); } finally { driver.quit(); }}
わんくま同盟 大阪勉強会 #48
public class BingSearch { private final RemoteWebDriver driver; public static BingSearch create(RemoteWebDriver driver) { BingSearch instance = new BingSearch(driver); driver.navigate().to("http://www.bing.com"); return instance; } private BingSearch(RemoteWebDriver driver) { this.driver = driver; } private WebElement txt 条件 () { return driver.findElementByName("q"); } private WebElement lbl 件数 () { return driver.findElementById("count"); }
わんくま同盟 大阪勉強会 #48
public void input 検索条件 (String 条件 ) { this.txt 条件 ().clear(); this.txt 条件 ().sendKeys( 条件 ); } public void submit 検索 () { this.txt 条件 ().submit(); Wait<WebDriver> wait = new WebDriverWait(driver, 10); wait.until(titleIs("Microsoft - Bing")); } public String get 検索結果件数 () { return this.lbl 件数 ().getText().replaceAll( "(.*of )(.*)( results)", "$2"); }}
Java だとプロパティ(getter) の
括弧 が鬱陶しい!
わんくま同盟 大阪勉強会 #48
便利なやつ ( その2 )
• PageFactory– PageObject の実装を少し楽にしてくれる– アノテーション / 属性 ベースで、要素と
フィールドのマッピング• WebElement 型の フィールド
– キャッシュも可能• ページ遷移 / リロードを行った場合は、キャッ
シュを取り直す必要あり– PageFactory.initElements で初期化を行う
わんくま同盟 大阪勉強会 #48
public class Top { public static Top create(WebDriver driver) { driver.navigate().to("http://www.bing.com"); Top instance = new Top(driver); PageFactory.initElements(driver, instance); return instance; } private final WebDriver driver; private Top(WebDriver driver) { this.driver = driver; }
@FindBy(name = "q") @CacheLookup private WebElement txt 条件 ;
わんくま同盟 大阪勉強会 #48
public Result search(String 条件 ) { this.txt 条件 .clear(); this.txt 条件 .sendKeys( 条件 ); this.txt 条件 .submit(); Wait<WebDriver> wait = new WebDriverWait(driver, 10); wait.until(titleIs( 条件 + " - Bing"));
return Result.create(driver); }}
わんくま同盟 大阪勉強会 #48
public class Result { public static Result create(WebDriver driver) { return PageFactory.initElements(driver, Result.class); } private final WebDriver driver; public Result(WebDriver driver) { this.driver = driver; }
@FindBy(id="count") private WebElement lbl 件数 ;
public String 検索結果件数 () { return lbl 件数 .getText() .replaceAll("(.*of )(.*)( results)", "$2"); }}
わんくま同盟 大阪勉強会 #48
便利なやつ ( その3 )
• Select– input type=“select” な WebElement のラッ
パー– Option の選択や取得を容易にしてくれる– PageFactory には対応していない
わんくま同盟 大阪勉強会 #48
WebElement select = ~ ;List<WebElement> options = select.findElements(By.tagName("option"));for (WebElement option : options) { option.click(); // 選択させる if (option.isSelected()) { // 選択していたら }}Select wrapper = new Select(select);// 選択させるwrapper.selectByIndex(0);wrapper.selectByValue("hoge");wrapper.selectByVisibleText(" フヒヒ ");// 選択されているもの取得wrapper.getFirstSelectedOption();wrapper.getAllSelectedOptions();
Select
わんくま同盟 大阪勉強会 #48
よく使いそうな操作
• Table 表示のデータ取得• スクリーンショットの取得• input type=“file” へのパス指定• Alert/Confirm ダイアログ• 新しい Window
わんくま同盟 大阪勉強会 #48
Table 表示のデータ取得
• Java だとこんな感じ…– 1 行を表すデータ型作成– <tr> 単位でループし、コレクションの取得
• PageFactory 等を使用しインスタンス生成–比較処理
• .NET だと…
わんくま同盟 大阪勉強会 #48
[Test]public void Table() { var driver = new ChromeDriver(@"c:\work"); try { driver.Url = "http://nabewebdriver.apphb.com/"; var wait = new WebDriverWait(driver, new TimeSpan(0, 0, 10)); wait.Until(d => d.Title == " メニュー "); driver.FindElementByPartialLinkText(" 検索画面 ").Click(); wait.Until(d => d.Title == "Search"); driver.FindElementByXPath("//input[@type='submit' and @value=' 検索 ']").Click(); wait.Until(ExpectedConditions.ElementIsVisible(By.Id("results"))); driver.FindElementsById("roop").Select(e => new { Name = e.FindElement(By.Id("resultName")).Text, Birthday = e.FindElement(By.Id("resultBirthday")).Text, Money = e.FindElement(By.Id("resultMoney")).Text }).Is( new { Name = "名前 1", Birthday = "1970/12/04", Money = "10,000" }, new { Name = "名前 2", Birthday = "1980/10/15", Money = "15,000" }, new { Name = "名前 3", Birthday = "1979/06/23", Money = "20,000" }, new { Name = "名前 4", Birthday = "1990/08/06", Money = "25,000" }, new { Name = "名前 5", Birthday = "2000/07/04", Money = "30,000" } ); } finally { driver.Quit(); }}
わんくま同盟 大阪勉強会 #48
driver.FindElementsById("roop").Select(e => new { Name = e.FindElement(By.Id("resultName")).Text, Birthday = e.FindElement(By.Id("resultBirthday")).Text, Money = e.FindElement(By.Id("resultMoney")).Text}).Is( new { Name = "名前 1", Birthday = "1970/12/04", Money = "10,000" }, new { Name = "名前 2", Birthday = "1980/10/15", Money = "15,000" }, new { Name = "名前 3", Birthday = "1979/06/23", Money = "20,000" }, new { Name = "名前 4", Birthday = "1990/08/06", Money = "25,000" }, new { Name = "名前 5", Birthday = "2000/07/04", Money = "30,000" } );
わんくま同盟 大阪勉強会 #48
スクリーンショットの取得
• TakesScreenshot を実装している Driver が対象–殆どの Driver は実装している
• Java だと…
• NET だと…
FileUtils.copyFile( driver.getScreenshotAs(OutputType.FILE), new File("c:/work/hoge.png"));
driver.GetScreenshot().SaveAsFile( @"c:\work\hoge.png", ImageFormat.Png);
わんくま同盟 大阪勉強会 #48
input type=“file” へのパス指定
• WebElement を取得し、 sendKeys でフルパスを渡す。
• Java だと…
• NET だと…
driver.findElement(By.id("fileupload")) .sendKeys("c:/work/result.jpg");
driver.FindElement(By.Id("fileupload")) .SendKeys(@"c:\work\result.jpg");
わんくま同盟 大阪勉強会 #48
Alert/Confirm ダイアログ
• WebDriver から TargetLocator を取得し、alert を呼ぶ。
• Java だと…
• NET だと…Alert dialog = driver.switchTo().alert();
var dialog = driver.SwitchTo().Alert();
わんくま同盟 大阪勉強会 #48
Alert インターフェース
• getText– 表示しているテキストを取得
• accept– OK ボタンクリック
• dismiss– キャンセル ボタンクリック
• sendKeys
わんくま同盟 大阪勉強会 #48
Alert/Confirm ダイアログ 注意点
• ダイアログが表示されている間は他の操作は出来ない– Driver や Element の操作
• accept/dismiss した後に Alert を操作 –閉じている Dialog は操作出来ない
• Alert を取得する際は、念のため Wait を使う
わんくま同盟 大阪勉強会 #48
エラーになるケース
HTML<input type="button" id="btn" value="hoge“ onclick="alert('hoge');" /><input type="text" id="txt" />
C# コードvar text = driver.FindElementById("txt");driver.FindElementById("btn").Click();// text.SendKeys("hoge"); // Error// driver.FindElementById("button"); // Errorvar dialog = driver.SwitchTo().Alert();dialog.Accept();var dialogText = dialog.Text; // Error
わんくま同盟 大阪勉強会 #48
Alert 取得時の Wait
• Java だと…
• NET だと…
Wait<WebDriver> wait = new WebDriverWait(driver, 10);Alert dialog = wait.until(alertIsPresent());
var wait = new WebDriverWait( driver, new TimeSpan(0, 0, 10));var dialog = wait.Until(d => { try { return d.SwitchTo().Alert(); } catch (Exception) { return null;}});
わんくま同盟 大阪勉強会 #48
新しい Window
• WebDriver から TargetLocator を取得し、window を呼ぶ。–戻り値は WebDriver で呼び出し元と同じイ
ンスタンス• Java だと…
• NET だと…driver.switchTo().window("name or handler");
driver.SwitchTo().Window("name"); // 一応 handler でも OK
わんくま同盟 大阪勉強会 #48
WindowHandler の取得
• Java だと…
• NET だと…
String current = driver.getWindowHandle();Set<String> all = driver.getWindowHandles();
var current = driver.CurrentWindowHandle;var all = driver.WindowHandles;
わんくま同盟 大阪勉強会 #48
新しい Window 切替時の Wait
• Java だと…
• NET だと…
Wait<WebDriver> wait = new WebDriverWait(driver, 10);wait.until(new Function<WebDriver, WebDriver>() { public WebDriver apply(WebDriver arg0) { try { return arg0.switchTo().window("windowName"); } catch (Exception e) { return null; } }});
var wait = new WebDriverWait( driver, new TimeSpan(0, 0, 10));wait.Until(d => { try { return d.SwitchTo().Window("windowName"); } catch (Exception) { return null;}});
わんくま同盟 大阪勉強会 #48
~まとめ~
わんくま同盟 大阪勉強会 #48
まとめ• 複数の言語 / ブラウザに対応している
– Java が API が充実してそう• 大概のことは出来ます
–今回説明省いたきましたが、 Interactions を使えば、 Drag&Drop みたいな事も
http://code.google.com/p/selenium/wiki/AdvancedUserInteractions#Mouse_interactions
• Wait 超大事!• 当然テスト以外でも使えます
– ブラウザ操作の自動化