JMeter (2) —— JMeter與WebDriver測試用户登陸以CAS SSO為例(101 Tutorial)
主要內容
- JMeter與WebDriver測試用户登陸以CAS SSO為例
環境與參考
jvm版本: 1.8.0_65
jmeter版本: 2.13
firefox版本: 39.0.3
準備
請參照JMeter (1) —— JMeter與WebDriver安裝與測試(101 Tutorial) 安裝好JMeter。
需要注意的是:
- JMeter的版本
- jar依賴衝突
- firefox版本
待測試的CAS環境
架構
配置
請參照以下文章搭建配置好CAS單點登陸的本地環境。
- CAS (1) —— Mac下配置CAS到Tomcat(服務端)
- CAS (2) —— Mac下配置CAS到Tomcat(客户端)
- CAS (6) —— Nginx代理模式下瀏覽器訪問CAS服務器網絡順序圖詳解
- CAS (7) —— Mac下配置CAS 4.x集羣及JPATicketRegistry(服務端)
測試
注意:我們這裏只是以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('')
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=""
wait.until(pkg.ExpectedConditions.presenceOfElementLocated(pkg.By.xpath("//a[@href='']")))
var results = WDS.browser.findElements(pkg.By.xpath("//a[@href='']"))
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.WebDriverScriptable
package 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.Cookie
package 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
結束