JMeter插件分類
GUI組件 | 非GUI組件 |
---|---|
ThreadGroup(線程組) Test Fragment(測試片段) logic Controller(邏輯控制器) Config element(配置元件) Timer(定時器) pre processor(前置處理器) post processor(后置處理器) Sampler(測試抽樣器) Assertion(斷言) Listener(監(jiān)聽器) |
Function(函數) |
JMeter Gui – TestElement約定
在編寫任何JMeter組件時其弊,必須注意某些特定的約定——如果JMeter環(huán)境中正確地運行JMeter組件癣朗,那么它將會運行碍侦。本部分描述了組件的GUI部分必須滿足的約定恨旱。
JMeter中的GUI代碼嚴格地與測試元件代碼(這里指邏輯控制代碼座慰,下同)分離往堡。因此械荷,當編寫一個組件時,將會有一個用于測試元件的類虑灰,另一個用于GUI表示吨瞎。GUI類是無狀態(tài)的,因此它不應該掛在對測試元件的引用上(盡管有例外)穆咐。
GUI元素應該繼承適當的抽象類:
AbstractSamplerGui
AbstractAssertionGui
AbstractConfigGui
AbstractControllerGui
AbstractPostProcessorGui
AbstractPreProcessorGui
AbstractVisualizer
AbstractTimerGui
……
這些抽象類提供了大量的管道工作颤诀,不用擴展,用來代替直接實現接口对湃。
已經繼承了適當的GUI類崖叫,剩下要遵循以下步驟:
- 實現getResourceLabel()
該方法返回資源的標題/名稱。
-
創(chuàng)建GUI
無論使用什么樣式熟尉,都要布局GUI归露。類最終要繼承JPanel洲脂,因此布局必須在的類自己的ContentPane中斤儿。不要通過動作和事件將·GUI元素連接到測試元件類。讓swing的內部模型盡可能多地掛在所有數據上恐锦。-
一些標準的GUI內容應該添加到所有JMeter GUI組件中:
調用setBorder(makeBorder())往果。這將給它提供標準的JMeter邊框。
通過makeTitlePanel()添加標題窗格一铅。通常這是添加到GUI中的第一件事陕贮,應該在一個垂直布局方案中完成,或者使用JMeter的VerticalLayout類潘飘。下面是一個示例init()方法:
-
-
實現public void configure(TestElement el)
一定調用super.configure(e)肮之,這將填充一些數據掉缺,比如元素的名稱
使用此方法將數據設置為GUI元素。如下例
- 實現public void modifyTestElement(TestElement e)戈擒,這是將數據從GUI元素移動到TestElement的地方眶明。這是前一種方法的邏輯逆操作
首先調用super.configureTestElement(e),處理一些默認數據筐高;如下圖
- 實現public TestElement createTestElement()搜囱,該方法應該創(chuàng)建TestElement類的一個新實例,然后將其傳遞modifyTestElement(TestElement)方法
不能保留對測試元件的引用的原因是因為JMeter的測試元件重用了多個GUI類對象的實例柑土。這樣可以節(jié)省很多內存蜀肘。它還使得編寫新組件的GUI部分變得非常容易。您仍然需要與Swing中的布局進行斗爭稽屏,但是不必擔心如何創(chuàng)建正確的事件和從GUI元素中獲取數據放入測試元件中扮宠。JMeter知道什么時候調用自定義配置,以及可以用一種非常簡單的方式來完成它的修改诫欠。
總結:
GUI與測試元件分離:GUI部分通過繼承各種組件GUI抽象類涵卵,測試元件部分通過繼承組件抽象類和實現各種接口方式從而實現不同組件的內部邏輯控制;
GUI與測試元件不分離:與分離方法的區(qū)別在于不單獨實現GUI部分荒叼,在測試元件部分通過實現TestBean接口方法從而實現對GUI界面的配置轿偎。(TestBean是一個空接口:Marker interface to tell JMeter to make a Test Bean Gui for the class)
JMeter插件組件實現
TestElement
是所有組件的最基本單元,組件類都是TestElement
類的子類
組件的實現需完成兩部分:GUI
和TestElement
GUI部分的實現
繼承并實現對應的抽象類
邏輯控制實現
Assertion(斷言)組件
Assertion
(斷言)組件通過繼承AbstractTestElement
抽象類(或者AbstractTestElement
子類)被廓,實現Assertion
接口的getResult(SampleResult result)
方法對結果內容進行判斷坏晦,從而實現斷言方法,用于對Sampler
組件所產生的抽樣采集結果內容進行斷言嫁乘。
如XPathAssertion
摘自Jmeter源碼
public class XPathAssertion extends AbstractScopedAssertion implements Serializable, Assertion {
@Override
public AssertionResult getResult(SampleResult response) {
// no error as default
AssertionResult result = new AssertionResult(getName());
result.setFailure(false);
result.setFailureMessage("");
byte[] responseData = null;
Document doc = null;
try {
if (isScopeVariable()){
String inputString=getThreadContext().getVariables().get(getVariableName());
if (!StringUtils.isEmpty(inputString)) {
responseData = inputString.getBytes(StandardCharsets.UTF_8);
}
} else {
responseData = response.getResponseData();
}
.....
}
Config(配置元件)組件
Config
(配置元件)組件相對其他組件比較特殊昆婿,通過繼承ConfigTestElement
類或只需要GUI
部分的實現即可完成本體任務
public class CSVDataSet extends ConfigTestElement
implements TestBean, LoopIterationListener, NoConfigMerge {
private static final Logger log = LoggerFactory.getLogger(CSVDataSet.class);
}
ThreadGroup(線程組)組件
ThreadGroup
(線程組)組件繼承AbstractThreadGroup
抽象類,通過重寫各類控制方法來達到控制和協(xié)調各線程(虛擬用戶)的行為蜓斧,線程組是構建一個性能測試模型的最基本組件.
public abstract class AbstractThreadGroup extends AbstractTestElement
implements Serializable, Controller, JMeterThreadMonitor, TestCompilerHelper {
...
/** {@inheritDoc} */
@Override
public boolean isDone() {
return getSamplerController().isDone();
}
/** {@inheritDoc} */
@Override
public Sampler next() {
return getSamplerController().next();
}
@Override
public void addTestElement(TestElement child) {
getSamplerController().addTestElement(child);
}
/**
* {@inheritDoc}
*/
@Override
public final boolean addTestElementOnce(TestElement child){
if (children.putIfAbsent(child, DUMMY) == null) {
addTestElement(child);
return true;
}
return false;
}
...
}
Timer(定時器)組件
Timer
(定時器)組件通過繼承AbstractTestElement
抽象類仓蛆,實現Timer
接口的delay()
方法來實現對時間的控制
public class ConstantTimer extends AbstractTestElement implements Timer, Serializable, LoopIterationListener {
.....
@Override
public long delay() {
return delay;
}
}
控制線程延時,即用來模仿思考時間(ThinkTime)或鍵盤時間(KeyTime)
控制線程行為挎春,如SyncTimer(同步計時器)看疙,就是內部利用CyclicBarrier來控制阻塞和釋放全部運行線程的邏輯行為,從而達到“集合點”的目的直奋。
pre processor(前置處理器)組件
pre processor
(前置處理器)組件通過繼承AbstractTestElement
抽象類能庆,實現PreProcessor
接口的process ()
方法控制邏輯
@GUIMenuSortOrder(Integer.MAX_VALUE)
public class BeanShellPreProcessor extends BeanShellTestElement
implements Cloneable, PreProcessor, TestBean
{
private static final Logger log = LoggerFactory.getLogger(BeanShellPreProcessor.class);
private static final long serialVersionUID = 5;
// can be specified in jmeter.properties
private static final String INIT_FILE = "beanshell.preprocessor.init"; //$NON-NLS-1$
@Override
protected String getInitFileProperty() {
return INIT_FILE;
}
@Override
public void process(){
final BeanShellInterpreter bshInterpreter = getBeanShellInterpreter();
if (bshInterpreter == null) {
log.error("BeanShell not found");
return;
}
JMeterContext jmctx = JMeterContextService.getContext();
Sampler sam = jmctx.getCurrentSampler();
try {
// Add variables for access to context and variables
bshInterpreter.set("sampler", sam);//$NON-NLS-1$
processFileOrScript(bshInterpreter);
} catch (JMeterException e) {
if (log.isWarnEnabled()) {
log.warn("Problem in BeanShell script. {}", e.toString());
}
}
}
@Override
public Object clone() {
return super.clone();
}
}
post processor(后置處理器)組件
post processor
(后置處理器)組件通過繼承AbstractTestElement
抽象類,實現PostProcessor
接口的process ()
方法控制邏輯
@GUIMenuSortOrder(Integer.MAX_VALUE)
public class BeanShellPostProcessor extends BeanShellTestElement
implements Cloneable, PostProcessor, TestBean
{
private static final Logger log = LoggerFactory.getLogger(BeanShellPostProcessor.class);
private static final long serialVersionUID = 5;
// can be specified in jmeter.properties
private static final String INIT_FILE = "beanshell.postprocessor.init"; //$NON-NLS-1$
@Override
protected String getInitFileProperty() {
return INIT_FILE;
}
@Override
public void process() {
JMeterContext jmctx = JMeterContextService.getContext();
SampleResult prev = jmctx.getPreviousResult();
if (prev == null) {
return; // TODO - should we skip processing here?
}
final BeanShellInterpreter bshInterpreter = getBeanShellInterpreter();
if (bshInterpreter == null) {
log.error("BeanShell not found");
return;
}
try {
// Add variables for access to context and variables
bshInterpreter.set("data", prev.getResponseData());//$NON-NLS-1$
processFileOrScript(bshInterpreter);
} catch (JMeterException e) {
if (log.isWarnEnabled()) {
log.warn("Problem in BeanShell script: {}", e.toString());
}
}
}
@Override
public Object clone() {
return super.clone();
}
}
Controller(控制器)組件
Controller(控制器)組件通過繼承GenericController類
foreach脚线,重寫isDone搁胆、next、nextIsNull、getIterCount渠旁、reInitialize攀例、initialize、triggerEndOfLoop
@GUIMenuSortOrder(5)
public class ForeachController extends GenericController implements Serializable, IteratingController {
/**
* {@inheritDoc}
*/
@Override
public boolean isDone() {
if (loopCount >= getEndIndex()) {
return true;
}
JMeterContext context = getThreadContext();
StringBuilder builder = new StringBuilder(
getInputVal().length()+getSeparator().length()+3);
String inputVariable =
builder.append(getInputVal())
.append(getSeparator())
.append(Integer.toString(loopCount+1)).toString();
final JMeterVariables variables = context.getVariables();
final Object currentVariable = variables.getObject(inputVariable);
if (currentVariable != null) {
variables.putObject(getReturnVal(), currentVariable);
if (log.isDebugEnabled()) {
log.debug("{} : Found in vars:{}, isDone:{}",
getName(), inputVariable, Boolean.FALSE);
}
return false;
}
return super.isDone();
}
// Prevent entry if nothing to do
@Override
public Sampler next() {
try {
if (breakLoop || emptyList()) {
resetBreakLoop();
reInitialize();
resetLoopCount();
return null;
}
return super.next();
} finally {
updateIterationIndex(getName(), loopCount);
}
}
/**
* {@inheritDoc}
*/
@Override
protected Sampler nextIsNull() throws NextIsNullException {
reInitialize();
// Conditions to reset the loop count
if (breakLoop
|| endOfArguments() // no more variables to iterate
|| loopCount >= getEndIndex() // we reached end index
) {
resetBreakLoop();
resetLoopCount();
return null;
}
return next();
}
/**
* {@inheritDoc}
*/
@Override
protected int getIterCount() {
return loopCount + 1;
}
/**
* {@inheritDoc}
*/
@Override
protected void reInitialize() {
setFirst(true);
resetCurrent();
incrementLoopCount();
recoverRunningVersion();
}
/**
* {@inheritDoc}
*/
@Override
public void triggerEndOfLoop() {
super.triggerEndOfLoop();
resetLoopCount();
}
/**
* Reset loopCount to Start index
* @see org.apache.jmeter.control.GenericController#initialize()
*/
@Override
public void initialize() {
super.initialize();
loopCount = getStartIndex();
}
@Override
public void startNextLoop() {
reInitialize();
}
@Override
public void breakLoop() {
breakLoop = true;
setFirst(true);
resetCurrent();
resetLoopCount();
recoverRunningVersion();
}
@Override
public void iterationStart(LoopIterationEvent iterEvent) {
reInitialize();
resetLoopCount();
}
}
Sampler(測試抽樣器)組件
Sampler
(測試抽樣器)組件繼承AbstractSampler
抽象類顾腊,通過重寫SampleResult sample(Entry e)
方法肛度,實現測試過程以及測試結果的采集功能。
@GUIMenuSortOrder(2)
public class DebugSampler extends AbstractSampler implements TestBean {
....
@Override
public SampleResult sample(Entry e) {
SampleResult res = new SampleResult();
res.setSampleLabel(getName());
res.sampleStart();
StringBuilder sb = new StringBuilder(100);
StringBuilder rd = new StringBuilder(20); // for request Data
if (isDisplayJMeterVariables()){
rd.append("JMeterVariables\n");
sb.append("JMeterVariables:\n");
formatSet(sb, JMeterContextService.getContext().getVariables().entrySet());
sb.append("\n");
}
}
....
}
Listener(監(jiān)聽器)
直接繼承AbstractTestElement
投慈,實現sampleListener
或Visualizer
等接口方法
@GUIMenuSortOrder(Integer.MAX_VALUE)
public class BeanShellListener extends BeanShellTestElement
implements Cloneable, SampleListener, TestBean, Visualizer, UnsharedComponent {
@Override
protected String getInitFileProperty() {
return INIT_FILE;
}
@Override
public void sampleOccurred(SampleEvent se) {
final BeanShellInterpreter bshInterpreter = getBeanShellInterpreter();
if (bshInterpreter == null) {
log.error("BeanShell not found");
return;
}
SampleResult samp=se.getResult();
try {
bshInterpreter.set("sampleEvent", se);//$NON-NLS-1$
bshInterpreter.set("sampleResult", samp);//$NON-NLS-1$
processFileOrScript(bshInterpreter);
} catch (JMeterException e) {
if (log.isWarnEnabled()) {
log.warn("Problem in BeanShell script. {}", e.toString());
}
}
}
@Override
public void sampleStarted(SampleEvent e) {
// NOOP
}
@Override
public void sampleStopped(SampleEvent e) {
// NOOP
}
@Override
public void add(SampleResult sample) {
// NOOP
}
@Override
public boolean isStats() { // Needed by Visualizer interface
return false;
}
@Override
public Object clone() {
return super.clone();
}
}
可以從實際用途上將其分為兩大類Report (報告)和Visualizers(監(jiān)視器)承耿。
Report (報告)繼承AbstractListenerElement抽象類,通過實現sampleOccurred(SampleEvent e)方法伪煤,對所有采集事件中所產生的SampleResult進行處理加袋,從而生成報告
Visualizers(監(jiān)視器)主要用于特定的監(jiān)控任務,比如監(jiān)控系統(tǒng)資源利用率的組件抱既,與Report的區(qū)別在于Visualizers必須繼承一個 ResultCollector類职烧,并在收集器中通過開啟額外線程方式完成自定義的數據采集。
Function(函數)
請查閱 Jmeter擴展自定義函數
附錄
JMeter一些GUI類繼承關系
轉載于
https://blog.csdn.net/yue530tomtom/article/details/77649872