• JMeter Gui – TestElement约定(转自约会言行的博客,链接:http://blog.csdn.net/yue530tomtom/article/details/77649872?locationNum=4&fps=1)


    在编写任何JMeter组件时,必须注意某些特定的约定——如果JMeter环境中正确地运行JMeter组件,那么它将会运行。本部分描述了组件的GUI部分必须满足的约定。 

    JMeter中的GUI代码严格地与测试元件代码(这里指逻辑控制代码,下同)分离。因此,当编写一个组件时,将会有一个用于测试元件的类,另一个用于GUI表示。GUI类是无状态的,因此它不应该挂在对测试元件的引用上(尽管有例外)。

    GUI元素应该继承适当的抽象类:

    • AbstractSamplerGui
    • AbstractAssertionGui
    • AbstractConfigGui
    • AbstractControllerGui
    • AbstractPostProcessorGui
    • AbstractPreProcessorGui
    • AbstractVisualizer
    • AbstractTimerGui
    • ……

    这些抽象类提供了大量的管道工作,不用扩展,用来代替直接实现接口。

    已经继承了适当的GUI类,剩下要遵循以下步骤: 

    1、实现getResourceLabel() 
    该方法返回资源的标题/名称。 
    2、创建GUI。无论使用什么样式,都要布局GUI。类最终要继承JPanel,因此布局必须在的类自己的ContentPane中。不要通过动作和事件将·GUI元素连接到测试元件类。让swing的内部模型尽可能多地挂在所有数据上。 
    (1)一些标准的GUI内容应该添加到所有JMeter GUI组件中: 
    a、调用setBorder(makeBorder())。这将给它提供标准的JMeter边框。 
    b、通过makeTitlePanel()添加标题窗格。通常这是添加到GUI中的第一件事,应该在一个垂直布局方案中完成,或者使用JMeter的VerticalLayout类。下面是一个示例init()方法:

    private void init() {
        setLayout(new BorderLayout());
        setBorder(makeBorder());
        Box box = Box.createVerticalBox();
        box.add(makeTitlePanel());
        box.add(makeSourcePanel());
        add(box,BorderLayout.NORTH);
        add(makeParameterPanel(),BorderLayout.CENTER);
    }

    3、实现public void configure(TestElement el) 
    (1)一定调用super.configure(e),这将填充一些数据,比如元素的名称 
    (2)使用此方法将数据设置为GUI元素。例子

    public void configure(TestElement el) {
        super.configure(el);
        useHeaders.setSelected(el.getPropertyAsBoolean(RegexExtractor.USEHEADERS));
        useBody.setSelected(!el.getPropertyAsBoolean(RegexExtractor.USEHEADERS));
        regexField.setText(el.getPropertyAsString(RegexExtractor.REGEX));
        templateField.setText(el.getPropertyAsString(RegexExtractor.TEMPLATE));
        defaultField.setText(el.getPropertyAsString(RegexExtractor.DEFAULT));
        matchNumberField.setText(el.getPropertyAsString(RegexExtractor.MATCH_NUM));
        refNameField.setText(el.getPropertyAsString(RegexExtractor.REFNAME));
    }

    (3)实现public void modifyTestElement(TestElement e),这是将数据从GUI元素移动到TestElement的地方。这是前一种方法的逻辑逆操作 
    a、调用super.configureTestElement(e),处理一些默认数据 
    b、例子

    public void modifyTestElement(TestElement e) {
        super.configureTestElement(e);
        e.setProperty(new BooleanProperty(
                RegexExtractor.USEHEADERS,
                useHeaders.isSelected()));
        e.setProperty(RegexExtractor.MATCH_NUMBER,
                matchNumberField.getText());
        if (e instanceof RegexExtractor) {
            RegexExtractor regex = (RegexExtractor)e;
            regex.setRefName(refNameField.getText());
            regex.setRegex(regexField.getText());
            regex.setTemplate(templateField.getText());
            regex.setDefaultValue(defaultField.getText());
        }
    }

    (4)实现public TestElement createTestElement(),该方法应该创建TestElement类的一个新实例,然后将其传递modifyTestElement(TestElement)方法

    public TestElement createTestElement() {
        RegexExtractor extractor = new RegexExtractor();
        modifyTestElement(extractor);
        return extractor;
    }

    不能保留对测试元件的引用的原因是因为JMeter的测试元件重用了多个GUI类对象的实例。这样可以节省很多内存。它还使得编写新组件的GUI部分变得非常容易。您仍然需要与Swing中的布局进行斗争,但是不必担心如何创建正确的事件和从GUI元素中获取数据放入测试元件中。JMeter知道什么时候调用自定义配置,以及可以用一种非常简单的方式来完成它的修改。

    总结:

    GUI与测试元件分离:GUI部分通过继承各种组件GUI抽象类,测试元件部分通过继承组件抽象类和实现各种接口方式从而实现不同组件的内部逻辑控制; 
    GUI与测试元件不分离:与分离方法的区别在于不单独实现GUI部分,在测试元件部分通过实现TestBean接口方法从而实现对GUI界面的配置。(TestBean是一个空接口:Marker interface to tell JMeter to make a Test Bean Gui for the class)

    jmeter插件分类

    GUI的组件主要包括10大组件

    • ThreadGroup(线程组)
    • Test Fragment(测试片段)
    • logic Controller(逻辑控制器)
    • Config element(配置元件)
    • Timer(定时器)
    • pre processor(前置处理器)
    • post processor(后置处理器)
    • Sampler(测试抽样器)
    • Assertion(断言)
    • Listener(监听器);

    非GUI组件

    • Function(函数)

    JMeter插件式组件实现

    TestElement是所有组件的最基本单元,组件类都是TestElement类的子类 
    依据上面介绍,组件的实现分两部分:GUI和TestElement

    GUI部分的实现

    继承实现对应的抽象类

    抽象类继承的类对应组件备注
    AbstractAssertionGui AbstractScopedJMeterGuiComponent 断言
    AbstractConfigGui AbstractJMeterGuiComponent 配置
    AbstractControllerGui AbstractJMeterGuiComponent 控制
    AbstractPostProcessorGui AbstractScopedJMeterGuiComponent 后置处理器
    AbstractPreProcessorGui AbstractJMeterGuiComponent 前置处理器
    AbstractSamplerGui AbstractJMeterGuiComponent 取样器
    AbstractThreadGroupGui AbstractJMeterGuiComponent 线程组
    AbstractTimerGui AbstractJMeterGuiComponent 定时器
    AbstractListenerGui AbstractJMeterGuiComponent 监听器
    AbstractVisualizer AbstractListenerGui 组件依赖
    AbstractScopedJMeterGuiComponent AbstractJMeterGuiComponent 组件依赖
    AbstractJMeterGuiComponent JPanel 组件依赖
    AbstractFunction Function 组件依赖
    AbstractJMeterGuiComponent JPanel 组件依赖

    逻辑控制实现

    Assertion(断言)组件

    Assertion(断言)组件通过继承AbstractTestElement抽象类(或者AbstractTestElement子类),实现Assertion接口的getResult(SampleResult result)方法对结果内容进行判断,从而实现断言方法,用于对Sampler组件所产生的抽样采集结果内容进行断言。

    比如:

    public class XMLSchemaAssertion extends AbstractTestElement implements Serializable, Assertion {
        ……
        @Override
        public AssertionResult getResult(SampleResult response) {
            AssertionResult result = new AssertionResult(getName());
            ……
            return result;
        }
        ……
    }

    再比如:

    public abstract class AbstractScopedTestElement extends AbstractTestElement {...}
    public abstract class AbstractScopedAssertion extends AbstractScopedTestElement{...}
    
    public class DurationAssertion extends AbstractScopedAssertion implements Serializable, Assertion {
        public static final String DURATION_KEY = "DurationAssertion.duration"; 
        @Override
        public AssertionResult getResult(SampleResult response) {
            ……
            return result;
        }
        private long getAllowedDuration() {
            return getPropertyAsLong(DURATION_KEY);
        }
        public void setAllowedDuration(long duration) {
            setProperty(DURATION_KEY, duration);
        }
    }

    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抽象类,通过重写各类控制方法来达到控制和协调各线程(虚拟用户)的行为,线程组是构建一个性能测试模型的最基本组件。

    public class ThreadGroupTest extends AbstractThreadGroup {
        private static final long serialVersionUID = 1L;
        @Override
        public void threadFinished(JMeterThread arg0) {}
        @Override
        public int numberOfActiveThreads() {return 0;}
        @Override
        public void start(int arg0, ListenerNotifier arg1, ListedHashTree arg2, StandardJMeterEngine arg3) {}
        @Override
        public void stop() {}
        @Override
        public boolean stopThread(String arg0, boolean arg1) {return false;}
        @Override
        public void tellThreadsToStop() {}
        @Override
        public boolean verifyThreadsStopped() {return false;}
        @Override
        public void waitThreadsStopped() {}
    }

    Timer(定时器)组件

    Timer(定时器)组件通过继承AbstractTestElement抽象类,实现Timer接口的delay()方法来实现对时间的控制

    public class TimerTest extends AbstractTestElement implements Timer{
        private static final long serialVersionUID = 1L;
        @Override
        public long delay() {
            return 0;
        }
    }

    控制线程延时,即用来模仿思考时间(ThinkTime)或键盘时间(KeyTime);

    控制线程行为,如SyncTimer(同步计时器),就是内部利用CyclicBarrier来控制阻塞和释放全部运行线程的逻辑行为,从而达到“集合点”的目的。

    public class SyncTimer extends AbstractTestElement implements Timer, Serializable, TestBean, TestStateListener, ThreadListener {
        private static final Logger log = LoggerFactory.getLogger(SyncTimer.class);
        private static class BarrierWrapper implements Cloneable {
            private CyclicBarrier barrier;
            public BarrierWrapper() {
                this.barrier = null;
            }
            public BarrierWrapper(int parties) {
                this.barrier = new CyclicBarrier(parties);
            }
            public synchronized void setup(int parties) {
                if(this.barrier== null) {
                    this.barrier = new CyclicBarrier(parties);
                }
            }
            public int await() throws InterruptedException, BrokenBarrierException{
                return barrier.await();
            }
            public int await(long timeout, TimeUnit timeUnit) throws InterruptedException, BrokenBarrierException, TimeoutException {
                return barrier.await(timeout, timeUnit);
            }
            public void reset() {
                barrier.reset();
            }
            @Override
            protected Object clone()  {
                BarrierWrapper barrierWrapper=  null;
                try {
                    barrierWrapper = (BarrierWrapper) super.clone();
                    barrierWrapper.barrier = this.barrier;
                } catch (CloneNotSupportedException e) {
                }
                return barrierWrapper;
            }
        }
        ……
        public void threadStarted() {
            if(getGroupSize() == 0) {
                int numThreadsInGroup = JMeterContextService.getContext().getThreadGroup().getNumThreads();
                // Unique Barrier creation ensured by synchronized setup
                this.barrier.setup(numThreadsInGroup);
            }
        }
        ……
    }

    pre processor(前置处理器)组件

    pre processor(前置处理器)组件通过继承AbstractTestElement抽象类,实现PreProcessor接口的process ()方法控制逻辑

    比如:BeanShellPreProcessor

    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();
        }
    }

    作用:对线程上下文中的当前Sampler和前一个SampleResult进行识别和判断。

    post processor(后置处理器)组件

    post processor(后置处理器)组件通过继承AbstractTestElement抽象类,实现PostProcessor接口的process ()方法控制逻辑

    public abstract class AbstractScopedTestElement extends AbstractTestElement {……}
    
    public class RegexExtractor extends AbstractScopedTestElement implements PostProcessor, Serializable {
        ……
      @Override
      public void process() {}
      ……
    }

    作用:对线程上下文中的前一个SampleResult进行识别和判断。

    Controller(控制器)组件

    Controller(控制器)组件通过继承GenericController类 
    比如foreach,重写isDone、next、nextIsNull、getIterCount、reInitialize、initialize、triggerEndOfLoop

    public class ForeachController extends GenericController implements Serializable {
        public ForeachController() {}
        ……
        @Override
        public boolean isDone() {}
        @Override
        public Sampler next() {}    
        @Override
        protected Sampler nextIsNull() throws NextIsNullException {}
        @Override
        protected int getIterCount() {return loopCount+1;}
        @Override
        protected void reInitialize() {}
        @Override
        public void triggerEndOfLoop() {}
        @Override
        public void initialize() {}
    }

    Sampler(测试抽样器)组件

    Sampler(测试抽样器)组件继承AbstractSampler抽象类,通过重写SampleResult sample(Entry e)方法,实现测试过程以及测试结果的采集功能。

    public class DebugSampler extends AbstractSampler implements TestBean {
        ……
        @Override
        public SampleResult sample(Entry e) {}
        ……
    }

    Listener(监听器)

    直接继承AbstractTestElement,实现sampleListener或Visualizer等接口方法

    public class Summariser extends AbstractTestElement
        implements Serializable, SampleListener, TestStateListener, NoThreadClone, Remoteable {
        ……
        @Override
        @SuppressWarnings("SynchronizeOnNonFinalField")
        public void sampleOccurred(SampleEvent e) {
            SampleResult s = e.getResult();
            if(IGNORE_TC_GENERATED_SAMPLERESULT && TransactionController.isFromTransactionController(s)) {
                return;
            }
            long now = System.currentTimeMillis() / 1000;// in seconds
            SummariserRunningSample myDelta = null;
            SummariserRunningSample myTotal = null;
            boolean reportNow = false;
            synchronized (myTotals) {
                if (s != null) {
                    myTotals.delta.addSample(s);
                }
                if ((now > myTotals.last + INTERVAL_WINDOW) && (now % INTERVAL <= INTERVAL_WINDOW)) {
                    reportNow = true;
                    // copy the data to minimise the synch time
                    myDelta = new SummariserRunningSample(myTotals.delta);
                    myTotals.moveDelta();
                    myTotal = new SummariserRunningSample(myTotals.total);
                    myTotals.last = now; // stop double-reporting
                }
            }
            if (reportNow) {
                formatAndWriteToLog(myName, myDelta, "+");
                // Only if we have updated them
                if (myTotal != null && myDelta != null &&myTotal.getNumSamples() != myDelta.getNumSamples()) { // NOSONAR
                    formatAndWriteToLog(myName, myTotal, "=");
                }
            }
        }
        ……
        }

    可以从实际用途上将其分为两大类Report (报告)和Visualizers(监视器)。 
    Report (报告)继承AbstractListenerElement抽象类,通过实现sampleOccurred(SampleEvent e)方法,对所有采集事件中所产生的SampleResult进行处理,从而生成报告;

    public class ResultCollector extends AbstractListenerElement implements SampleListener, Clearable, Serializable,
            TestStateListener, Remoteable, NoThreadClone {
        ……
        @Override
        public void sampleOccurred(SampleEvent event) {
            SampleResult result = event.getResult();
    
            if (isSampleWanted(result.isSuccessful())) {
                sendToVisualizer(result);
                if (out != null && !isResultMarked(result) && !this.isStats) {
                    SampleSaveConfiguration config = getSaveConfig();
                    result.setSaveConfig(config);
                    try {
                        if (config.saveAsXml()) {
                            SaveService.saveSampleResult(event, out);
                        } else { // !saveAsXml
                            String savee = CSVSaveService.resultToDelimitedString(event);
                            out.println(savee);
                        }
                    } catch (Exception err) {
                        log.error("Error trying to record a sample", err); // should throw exception back to caller
                    }
                }
            }
            if(summariser != null) {
                summariser.sampleOccurred(event);
            }
        }
        ……
    }

    Visualizers(监视器)主要用于特定的监控任务,比如监控系统资源利用率的组件,与Report的区别在于Visualizers必须继承一个 ResultCollector类,并在收集器中通过开启额外线程方式完成自定义的数据采集。

    public class ResultCollector extends AbstractListenerElement implements SampleListener, Clearable, Serializable, TestStateListener, Remoteable, NoThreadClone {……}

    比如标准插件中自定义一个JMXMonCollector

    public class CorrectedResultCollector extends ResultCollector {}
    
    public class JMXMonCollector extends CorrectedResultCollector implements Runnable, JMXMonSampleGenerator {……}
    注意:对于一个需要配置的组件类则需要实现ConfigMergabilityIndicator接口的public boolean applies(ConfigTestElement configElement)方法,用来指明哪些Config组件可以用来对其进行配置

    这里参考DebugSampler的源代码如下:

    public class DebugSampler extends AbstractSampler implements TestBean {
        ……
        private static final Set<String> APPLIABLE_CONFIG_CLASSES = new HashSet<>(
                Arrays.asList("org.apache.jmeter.config.gui.SimpleConfigGui"));
        @Override
        public boolean applies(ConfigTestElement configElement) {
            String guiClass = configElement.getProperty(TestElement.GUI_CLASS).getStringValue();
            return APPLIABLE_CONFIG_CLASSES.contains(guiClass);
        }
        ……
    }

    以上代码指明SimpleConfigGui配置元件可以对DebugSampler组件进行配置。

    函数

    Function(函数)是非GUI组件,这类组件的实现比较简单,而且功能比较单一,只需要继承相应的抽象类即可~

    写一个求字符长度的函数:StrLen

    public class StrLen extends AbstractFunction {
        private static final List<String> desc = new LinkedList<String>();
        private static final String KEY = "__strLen";
        static {
            desc.add("String to measure length");
            desc.add("Name of variable in which to store the result (optional)");
        }
        private Object[] values;
        public StrLen() {
        }
        @Override
        public synchronized String execute(SampleResult previousResult, Sampler currentSampler)
                throws InvalidVariableException {
            JMeterVariables vars = getVariables();
            Integer len = ((CompoundVariable) values[0]).execute().length();
            if (vars != null && values.length > 1) {
                String varName = ((CompoundVariable) values[1]).execute().trim();
                vars.put(varName, len.toString());
            }
            return len.toString();
        }
        @Override
        public synchronized void setParameters(Collection<CompoundVariable> parameters) throws InvalidVariableException {
            checkMinParameterCount(parameters, 1);
            values = parameters.toArray();
        }
        @Override
        public String getReferenceKey() {
            return KEY;
        }
        public List<String> getArgumentDesc() {
            return desc;
        }
    }

    附录

    jmeter一些GUI类继承关系 

    GUI类继承的类
    ResultActionGui AbstractPostProcessorGui
    ResultSaverGui AbstractListenerGui
    SummariserGui AbstractListenerGui
    TestBeanGUI AbstractJMeterGuiComponent
    ThreadGroupGui AbstractThreadGroupGui
    SetupThreadGroupGui ThreadGroupGui
    PostThreadGroupGui ThreadGroupGui
    WorkBenchGui AbstractJMeterGuiComponent
    AssertionGui AbstractAssertionGui
    BeanShellAssertionGui AbstractAssertionGui
    urationAssertionGui AbstractAssertionGui
    HTMLAssertionGui AbstractAssertionGui
    MD5HexAssertionGUI AbstractAssertionGui
    SizeAssertionGui AbstractAssertionGui
    SMIMEAssertionGui AbstractAssertionGui
    XMLAssertionGui AbstractAssertionGui
    XMLSchemaAssertionGUI AbstractAssertionGui
    XPathAssertionGui AbstractAssertionGui
    CriticalSectionControllerGui AbstractControllerGui
    IncludeControllerGui AbstractControllerGui
    InterleaveControlGui AbstractControllerGui
    ModuleControllerGui AbstractControllerGui
    OnceOnlyControllerGui AbstractControllerGui
    RandomControlGui AbstractControllerGui
    RandomOrderControllerGui LogicControllerGui
    SwitchControllerGui AbstractControllerGui
    ThroughputControllerGui AbstractControllerGui
    HtmlExtractorGui AbstractPostProcessorGui
    RegexExtractorGui AbstractPostProcessorGui
    XPathExtractorGui AbstractPostProcessorGui
    JSONPostProcessorGui AbstractPostProcessorGui
    CounterConfigGui AbstractConfigGui
    SampleTimeoutGui AbstractPreProcessorGui
    UserParametersGui AbstractPreProcessorGui
    TestActionGui AbstractSamplerGui
    AbstractRandomTimerGui AbstractTimerGui
    ConstantTimerGui AbstractTimerGui
    GaussianRandomTimerGui AbstractRandomTimerGui
    PoissonRandomTimerGui AbstractRandomTimerGui
    UniformRandomTimerGui AbstractRandomTimerGui
    AbstractAssertionGui AbstractScopedJMeterGuiComponent
    AbstractConfigGui AbstractJMeterGuiComponent
    LoginConfigGui AbstractConfigGui
    ObsoleteGui AbstractJMeterGuiComponent
    SimpleConfigGui AbstractConfigGui
    AbstractControllerGui AbstractJMeterGuiComponent
    LogicControllerGui AbstractControllerGui
    RunTimeGui AbstractControllerGui
    TestFragmentControllerGui AbstractControllerGui
    TestPlanGui AbstractJMeterGuiComponent
    TransactionControllerGui AbstractControllerGui
    WhileControllerGui AbstractControllerGui
    WorkBenchGui AbstractJMeterGuiComponent
    FunctionHelper JDialog
    AbstractJMeterGuiComponent JPanel
    AbstractScopedJMeterGuiComponent AbstractJMeterGuiComponent
    AbstractPostProcessorGui AbstractScopedJMeterGuiComponent
    AbstractPreProcessorGui AbstractJMeterGuiComponent
    ResultActionGui AbstractPostProcessorGui
    ResultSaverGui AbstractListenerGui
    SummariserGui AbstractListenerGui
    AbstractSamplerGui AbstractJMeterGuiComponent
    TestBeanGUI AbstractJMeterGuiComponent
    AbstractThreadGroupGui AbstractJMeterGuiComponent
    PostThreadGroupGui ThreadGroupGui
    SetupThreadGroupGui ThreadGroupGui
    ThreadGroupGui AbstractThreadGroupGui
    AbstractTimerGui AbstractJMeterGuiComponent
    AbstractListenerGui AbstractJMeterGuiComponent
    AbstractVisualizer AbstractListenerGui
  • 相关阅读:
    基于Visual C++2013拆解世界五百强面试题--题13-找最大公共子字符串
    基于Visual C++2013拆解世界五百强面试题--题12-进制转换
    Color颜色对照表
    根据选择打开相应的程序
    复制一个5G文件只需要两秒,全网最牛方法!
    判断一个男人穷还是富,只看这几点!
    判断一个男人穷还是富,只看这几点!
    判断一个男人穷还是富,只看这几点!
    16年国庆假期期间兼职所悟
    16年国庆假期期间兼职所悟
  • 原文地址:https://www.cnblogs.com/yigui/p/7615635.html
Copyright © 2020-2023  润新知