JMeter (2) —— JMeter與WebDriver測試使用者登陸以CAS SSO為例(101 Tutorial)
主要內容
- JMeter與WebDriver測試使用者登陸以CAS SSO為例
環境與參考
jvm版本: 1.8.0_65
jmeter版本: 2.13
firefox版本: 39.0.3
參考來源:
Leverage your Load Testing using JMeter and Selenium WebDriver
jmeter-plugins.org:FirefoxDriverConfig
jmeter-plugins.org:WebDriverTutorial
jmeter-plugins.org:WebDriverSampler
Using Selenium with JMeter's WebDriver Sampler
準備
請參照JMeter (1) —— JMeter與WebDriver安裝與測試(101 Tutorial) 安裝好JMeter。
需要注意的是:
- JMeter的版本
- jar依賴衝突
- firefox版本
待測試的CAS環境
架構
配置
請參照以下文章搭建配置好CAS單點登陸的本地環境。
測試
注意:我們這裡只是以CAS單點登陸為應用場景進行測試,此測試可以推廣到其他的web應用的登陸場景,也可以擴充套件到更為豐富的流程或場景中。
準備
新建測試計劃(TestPlan)
為測試計劃新增執行緒組(TheadGroup)
依次新增
- jp@gc - Firefox Driver Config (Thread Group -> Config Element)
- jp@gc - WebDriver Sampler (Thread Group -> Sampler)
- View Results Tree (Thread Group -> Listender)
- View Results in Table (Thread Group -> Listender)
測試本地的CAS單點登陸環境
指令碼
try { var pkg = JavaImporter(org.openqa.selenium, org.openqa.selenium.support.ui) WDS.log.info('WDS Name:' + WDS.name) WDS.sampleResult.sampleStart() WDS.browser.navigate().to('https://app2.hoau.com:8423/cas2') WDS.log.info('Browser Title:' + WDS.browser.getTitle()) WDS.log.info('Browser CurrentUrl:' + WDS.browser.getCurrentUrl()) WDS.log.info('Cookie:' + WDS.browser.manage().getCookies()) WDS.log.info('Request Header: ' + WDS.sampleResult.getRequestHeaders()) var what = WDS.browser.findElement(pkg.By.id('username')) what.sendKeys(['test01']) var where = WDS.browser.findElement(pkg.By.id('password')) where.sendKeys(['psw01']) // var button = WDS.browser.findElement(pkg.By.cssSelector('.btn-submit')) var button = WDS.browser.findElement(pkg.By.xpath("//input[@type='submit']")) button.click() var wait = new pkg.WebDriverWait(WDS.browser, 10) wait.until(pkg.ExpectedConditions.presenceOfElementLocated(pkg.By.xpath("//a[@title='Click here to log out']"))) var results = WDS.browser.findElements(pkg.By.xpath("//a[@title='Click here to log out']")) WDS.log.info('Result: ' + results) if(results.empty) { WDS.sampleResult.successful = false WDS.sampleResult.responseMessage = 'There were no results returned' } var iter = results.iterator() var element = iter.next() WDS.log.info('User: ' + element.getText()) if('test01' != element.getText()) { WDS.sampleResult.successful = false WDS.sampleResult.responseMessage = 'Login Failure' } WDS.sampleResult.sampleEnd() } catch(ex) { WDS.log.error(ex) WDS.sampleResult.successful = false WDS.sampleResult.responseMessage = 'There were no results returned' WDS.sampleResult.sampleEnd() }
執行
檢視“View Results in Table”
檢視“View Results Tree”
需要注意的是,諸如JMeter + WebDriver Plugin或Selenium Grid + Remote WebDriver抑或Sahi Web UI這些方式的測試,都是試圖模擬使用者從終端(瀏覽器)與伺服器進行真實互動,以上這些方式長於流程、場景,而非服務端的壓力(當然也可用於服務端壓力測試之目的,這裡強調的只是意圖),如果要對服務端進行壓力測試,我們可以有其他更為適合的方式。
測試cnblogs登陸
我們同樣可以cnblogs為目標系統,嘗試用JMeter來測試cnblogs的登陸。
指令碼
try { var pkg = JavaImporter(org.openqa.selenium, org.openqa.selenium.support.ui) WDS.log.info('WDS Name:' + WDS.name) WDS.sampleResult.sampleStart() WDS.browser.navigate().to('http://passport.cnblogs.com/user/signin') WDS.log.info('Browser Title:' + WDS.browser.getTitle()) WDS.log.info('Browser CurrentUrl:' + WDS.browser.getCurrentUrl()) WDS.log.info('Cookie:' + WDS.browser.manage().getCookies()) WDS.log.info('Request Header: ' + WDS.sampleResult.getRequestHeaders()) var what = WDS.browser.findElement(pkg.By.id('input1')) what.sendKeys(['weizhe_2008']) var where = WDS.browser.findElement(pkg.By.id('input2')) where.sendKeys(['********']) var button = WDS.browser.findElement(pkg.By.id('signin')) button.click() var wait = new pkg.WebDriverWait(WDS.browser, 1) // a href="http://home.cnblogs.com/u/richaaaard/" wait.until(pkg.ExpectedConditions.presenceOfElementLocated(pkg.By.xpath("//a[@href='http://www.cnblogs.com/richaaaard/']"))) var results = WDS.browser.findElements(pkg.By.xpath("//a[@href='http://www.cnblogs.com/richaaaard/']")) WDS.log.info('Result: ' + results) if(results.empty) { WDS.sampleResult.successful = false WDS.sampleResult.responseMessage = 'There were no results returned' } WDS.sampleResult.sampleEnd() } catch(ex) { WDS.log.error(ex) WDS.sampleResult.successful = false WDS.sampleResult.responseMessage = 'There were no results returned' WDS.sampleResult.sampleEnd() }
執行
結果
問題
由於此種測試模式下,我們會使用JMeter配置的Firefox Driver Config元件,同時使用WDS(Web Driver Sampler)Script在JMeter UI環境下測試目標應用,當Thread Group 的迴圈次數(Loop Count)或執行緒數(Number of Threads (user))的值大於1時,當前執行緒關聯的瀏覽器代理例項一直處於生命期,而單點登陸會保留使用者的登陸資訊,無需反覆輸入使用者名稱密碼,瀏覽器保持了這些資訊,因此後面斷言會出現錯誤,指令碼無法反覆執行從而達到為“驗證使用者名稱密碼這個流程”進行壓力測試的目的。
經研究,當前版本的JMeter及其使用的Selenium WebDriver Plugin除PhantomJS外,不支援動態修改Session Cookie,而網上多數文章介紹的相關Cookie的API,無法對瀏覽器Session Cookie進行操作。
WDS.browser.manage().getCookies() WDS.browser.manage().deleteAllCookies()
我們會使用HTTPs錄製的方式來解決壓力測試的問題。
擴充套件
為了解WDS 在JMeterUI下javascript指令碼的能力,以及涉及到面問題相關的原始碼。
WDS (JMeterPlugins-WebDriver.jar)
com.googlecode.jmeter.plugins.webdriver.sampler.WebDriverSampler
com.googlecode.jmeter.plugins.webdriver.sampler.WebDriverScriptablepackage com.googlecode.jmeter.plugins.webdriver.sampler; import org.apache.jmeter.samplers.SampleResult; import org.apache.log.Logger; import org.openqa.selenium.WebDriver; public final class WebDriverScriptable { private static final String[] EMPTY_ARGS = new String[0]; private String name; private String parameters; private Logger log; private WebDriver browser; private SampleResult sampleResult; public void setName(String name) { this.name = name; } public String getName() { return this.name; } public void setParameters(String parameters) { this.parameters = parameters; } public String getParameters() { return this.parameters; } public String[] getArgs() { return this.parameters != null ? this.parameters.trim().replaceAll("\\s+", " ").split(" ") : EMPTY_ARGS; } public void setLog(Logger log) { this.log = log; } public Logger getLog() { return this.log; } public void setBrowser(WebDriver browser) { this.browser = browser; } public WebDriver getBrowser() { return this.browser; } public void setSampleResult(SampleResult sampleResult) { this.sampleResult = sampleResult; } public SampleResult getSampleResult() { return this.sampleResult; } }
WDS.browser (WebDriver - selenium-api-2.47.0.jar)
org.openqa.selenium.WebDriver
org.openqa.selenium.Cookiepackage org.openqa.selenium; import java.net.URL; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import org.openqa.selenium.logging.Logs; public abstract interface WebDriver extends SearchContext { public abstract void get(String paramString); public abstract String getCurrentUrl(); public abstract String getTitle(); public abstract List<WebElement> findElements(By paramBy); public abstract WebElement findElement(By paramBy); public abstract String getPageSource(); public abstract void close(); public abstract void quit(); public abstract Set<String> getWindowHandles(); public abstract String getWindowHandle(); public abstract TargetLocator switchTo(); public abstract Navigation navigate(); public abstract Options manage(); @Beta public static abstract interface Window { public abstract void setSize(Dimension paramDimension); public abstract void setPosition(Point paramPoint); public abstract Dimension getSize(); public abstract Point getPosition(); public abstract void maximize(); } public static abstract interface ImeHandler { public abstract List<String> getAvailableEngines(); public abstract String getActiveEngine(); public abstract boolean isActivated(); public abstract void deactivate(); public abstract void activateEngine(String paramString); } public static abstract interface Navigation { public abstract void back(); public abstract void forward(); public abstract void to(String paramString); public abstract void to(URL paramURL); public abstract void refresh(); } public static abstract interface TargetLocator { public abstract WebDriver frame(int paramInt); public abstract WebDriver frame(String paramString); public abstract WebDriver frame(WebElement paramWebElement); public abstract WebDriver parentFrame(); public abstract WebDriver window(String paramString); public abstract WebDriver defaultContent(); public abstract WebElement activeElement(); public abstract Alert alert(); } public static abstract interface Timeouts { public abstract Timeouts implicitlyWait(long paramLong, TimeUnit paramTimeUnit); public abstract Timeouts setScriptTimeout(long paramLong, TimeUnit paramTimeUnit); public abstract Timeouts pageLoadTimeout(long paramLong, TimeUnit paramTimeUnit); } public static abstract interface Options { public abstract void addCookie(Cookie paramCookie); public abstract void deleteCookieNamed(String paramString); public abstract void deleteCookie(Cookie paramCookie); public abstract void deleteAllCookies(); public abstract Set<Cookie> getCookies(); public abstract Cookie getCookieNamed(String paramString); public abstract WebDriver.Timeouts timeouts(); public abstract WebDriver.ImeHandler ime(); @Beta public abstract WebDriver.Window window(); @Beta public abstract Logs logs(); } }
以上要注意內部靜態類 Options裡面提供的,對Cookie進行操作的API