JMeter取样器插件开发
背景:**需用通过jmeter这个测试工具去测试某系统接口,该系统已知有自己的sdk和自己的协议,暴露出来的接口还是c++开发的,具体的还没涉及到,总之jmeter现有提供的功能不满足测试要求,需要在jmeter上开发出新的插件。
调查:插件开发这个说法比较广泛,这个范围太大了,对着现有功能的页面分析了下,需要开发出自定义取样器,自定义页面来接收测试人员录入的参数,启动测试,执行自定义的接口逻辑处理(具体的处理逻辑还待定),然后根据要求返回出结果值。
初步目标:按照jmeter的sample开发要求,自定义界面布局,将自定义界面值传入自定义处理逻辑中。
参考网址:https://www.cnblogs.com/qiaoyeye/p/8146483.html
之前辛辛苦苦搭建起来的jmeter开发环境发现用不到了,因为sample插件开发,是maven功能里做的,然后打成jar包,放入\lib\ext下,就可以使用了。
1、新建maven工程
2、pom文件引入jmeter的核心包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>jmeterplugintest</groupId>
<artifactId>jmeterplugintest</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>jmeterplugntest</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jmeter-version>3.1</jmeter-version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_core</artifactId>
<version>${jmeter-version}</version>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_java</artifactId>
<version>${jmeter-version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
</dependency>
</dependencies>
<build>
<finalName>ssmtest</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.7</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.doxia</groupId>
<artifactId>doxia-site-renderer</artifactId>
<version>1.8</version>
</dependency>
</dependencies>
</plugin>
<!-- 这块用来控制maven打包,是否将依赖的包打进来
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.5</version>
<configuration>
<archive>
<manifest>
<mainClass>com.yj.TCPClient.upload.App</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>-->
<!-- 添加此项后,可直接使用mvn package | mvn install -->
<!-- 不添加此项,需直接使用mvn package assembly:single -->
<!-- <executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
-->
</plugins>
</build>
</project>
3、代码开发,分两块,一块是 界面布局开发,继承AbstractSamplerGui,用来设置界面控件和布局;另一块取样器逻辑开发,继承AbstractSampler,用来处理数据、请求接口、返回值处理。
MyPluginGUI.java
package com.test.gui;
import com.test.sampler.MyPluginSampler;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.config.gui.ArgumentsPanel;
import org.apache.jmeter.gui.util.HorizontalPanel;
import org.apache.jmeter.gui.util.JSyntaxTextArea;
import org.apache.jmeter.gui.util.JTextScrollPane;
import org.apache.jmeter.gui.util.VerticalPanel;
import org.apache.jmeter.protocol.java.config.JavaConfig;
import org.apache.jmeter.protocol.java.sampler.JavaSampler;
import org.apache.jmeter.samplers.gui.AbstractSamplerGui;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.property.BooleanProperty;
import org.apache.jmeter.testelement.property.TestElementProperty;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.gui.JLabeledChoice;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
public class MyPluginGUI extends AbstractSamplerGui implements ItemListener {
private static final long serialVersionUID=20l;
//IP
private JTextField domain;
//端口号
private JTextField port;
//encoding
private JTextField contentEncoding;
//路径
private JTextField path;
//keepalive
private JCheckBox useKeepAlive;
//method POST,GET
private JLabeledChoice method;
/** A panel allowing the user to set arguments for this test. */
//动态可添加参数
private ArgumentsPanel argsPanel;
//IP页面设置
private JPanel getDomainPanel(){
domain=new JTextField(10);
JLabel label=new JLabel("IP");
label.setLabelFor(domain);
JPanel panel=new HorizontalPanel();
panel.add(label, BorderLayout.WEST);
panel.add(domain,BorderLayout.CENTER);
return panel;
}
//端口设置
private JPanel getPortPanel(){
port=new JTextField(10);
JLabel label=new JLabel(JMeterUtils.getResString("web_server_port"));
label.setLabelFor(port);
JPanel panel=new HorizontalPanel();
panel.add(label,BorderLayout.WEST);
panel.add(port,BorderLayout.CENTER);
return panel;
}
//contentEncoding
private JPanel getContentEncodingPanel(){
contentEncoding=new JTextField(10);
JLabel label=new JLabel("contentEncoding");
label.setLabelFor(contentEncoding);
JPanel panel=new HorizontalPanel();
panel.add(label,BorderLayout.WEST);
panel.add(contentEncoding,BorderLayout.CENTER);
return panel;
}
//路径
private JPanel getPathPanel(){
path=new JTextField(10);
JLabel label=new JLabel(JMeterUtils.getResString("path"));
label.setLabelFor(path);
JPanel panel=new HorizontalPanel();
panel.add(label,BorderLayout.WEST);
panel.add(path,BorderLayout.CENTER);
return panel;
}
//keepalive 和 method
private Component getMethodAndKeepAlive(){
useKeepAlive =new JCheckBox(JMeterUtils.getResString("use_keepalive"));
useKeepAlive.setFont(null);
useKeepAlive.setSelected(true);
JPanel optionPanel=new HorizontalPanel();
optionPanel.setMinimumSize(optionPanel.getPreferredSize());
optionPanel.add(useKeepAlive);
String Marry[]={"GET","POST"};
method=new JLabeledChoice(JMeterUtils.getResString("method"),Marry,true,false);
JPanel methodPanel=new HorizontalPanel();
methodPanel.setLayout(new BoxLayout(methodPanel,BoxLayout.Y_AXIS));
methodPanel.add(optionPanel,BorderLayout.WEST);
methodPanel.add(method,BorderLayout.WEST);
return methodPanel;
}
/**
* Create a panel containing components allowing the user to provide
* arguments to be passed to the test class instance.
*
* @return a panel containing the relevant components
*/
private JPanel createParameterPanel() {
argsPanel = new ArgumentsPanel(JMeterUtils.getResString("paramtable")); // $NON-NLS-1$
return argsPanel;
}
//页面构建
public void createPanel(){
JPanel settingPanel=new VerticalPanel();
settingPanel.add(getDomainPanel());
settingPanel.add(getPortPanel());
settingPanel.add(getContentEncodingPanel());
settingPanel.add(getPathPanel());
settingPanel.add(getMethodAndKeepAlive());
// settingPanel.add(getPostBodyContent());
JPanel dataPanel=new JPanel(new BorderLayout(5,0));
dataPanel.add(settingPanel,BorderLayout.NORTH);
dataPanel.add(createParameterPanel(), BorderLayout.CENTER);
setLayout(new BorderLayout(0,5));
setBorder(makeBorder());
add(makeTitlePanel(),BorderLayout.NORTH);
add(dataPanel,BorderLayout.CENTER);
}
private void init(){
createPanel();
}
public MyPluginGUI(){
super();
init();
}
@Override
public String getLabelResource() {
throw new IllegalStateException("this is should not be called");
}
@Override
public String getStaticLabel(){
return "sample";
}
@Override
public void clearGui(){
super.clearGui();
argsPanel.clearGui();
domain.setText("");
port.setText("");
contentEncoding.setText("");
path.setText("");
// postBodyContent.setText("");
method.setText("GET");
useKeepAlive.setSelected(true);
}
/**
* 创建一个sampler,然后将界面中的数据设置到这个新的sampler实例中
* @return
*/
@Override
public TestElement createTestElement() {
MyPluginSampler sampler=new MyPluginSampler();
modifyTestElement(sampler);
return sampler;
}
/**
* 把界面上的数据移到sample中
* @param testElement
*/
@Override
public void modifyTestElement(TestElement testElement) {
testElement.clear();
configureTestElement(testElement);
//键值对
testElement.setProperty(new TestElementProperty(MyPluginSampler.ARGUMENTS, (Arguments) argsPanel.createTestElement()));
testElement.setProperty(MyPluginSampler.domain,domain.getText());
testElement.setProperty(MyPluginSampler.port,port.getText());
testElement.setProperty(MyPluginSampler.contentEncoding,contentEncoding.getText());
testElement.setProperty(MyPluginSampler.path,path.getText());
testElement.setProperty(MyPluginSampler.useKeepAlive,useKeepAlive.getText());
// testElement.setProperty(MyPluginSampler.postBodyContent,postBodyContent.getText());
testElement.setProperty(new BooleanProperty(MyPluginSampler.useKeepAlive,useKeepAlive.isSelected()));
testElement.setProperty(MyPluginSampler.method,method.getText());
}
@Override
public void configure(TestElement element){
super.configure(element);
//jmeter运行后,保存参数,不然执行后,输入框会清空
argsPanel.configure((Arguments) element.getProperty(MyPluginSampler.ARGUMENTS).getObjectValue());
domain.setText(element.getPropertyAsString(MyPluginSampler.domain));
port.setText(element.getPropertyAsString(MyPluginSampler.port));
contentEncoding.setText(element.getPropertyAsString(MyPluginSampler.contentEncoding));
path.setText(element.getPropertyAsString(MyPluginSampler.path));
// postBodyContent.setText(element.getPropertyAsString(MyPluginSampler.postBodyContent));
method.setText("GET");
useKeepAlive.setSelected(true);
}
/**
* Invoked when an item has been selected or deselected by the user.
* The code written for this method performs the operations
* that need to occur when an item is selected (or deselected).
*
* @param e
*/
@Override
public void itemStateChanged(ItemEvent e) {
}
}
注:ArgumentsPanel 为下面表格参数页面,可动态添加,这个找了好一会儿才寻来的
```clike
MyPluginSampler.java
package com.test.sampler;
import net.minidev.json.JSONObject;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.samplers.AbstractSampler;
import org.apache.jmeter.samplers.Entry;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import java.io.Serializable;
import java.util.concurrent.atomic.AtomicInteger;
public class MyPluginSampler extends AbstractSampler implements Serializable {
private static final long serialVersionUID=230l;
private static final Logger log= LoggingManager.getLoggerForClass();
//页面属性数据存放
public static final String domain="domain.text";
public static final String port="port.text";
public static final String contentEncoding="contentEncoding.text";
public static final String path="path.text";
public static final String method="method.text";
public static final String useKeepAlive="useKeepAlive.text";
// public static final String postBodyContent="postBodyContent.text";
public static final String ARGUMENTS = "mypluginSampler.Arguments"; // $NON-NLS-1$
private static AtomicInteger classCount=new AtomicInteger(0);//keep track of classes created
private String getTitle(){
return this.getName();
}
//从控制面板获取domain输入的数据
public String getDomain(){
return getPropertyAsString(domain);
}
//从控制面板获取端口的输入的数据
public String getPort(){
return getPropertyAsString(port);
}
//从控制面板获取contentEncoding输入的数据
public String getContentEncoding(){
return getPropertyAsString(contentEncoding);
}
//从控制面板获取path输入的数据
public String getPath(){
return getPropertyAsString(path);
}
//从gui获取method输入的数据
public String getMethod(){
return getPropertyAsString(method);
}
//从gui获取useKeepAlive的值
public String getUseKeepAlive(){
return getPropertyAsString(useKeepAlive);
}
// //从gui获取postBody输入的数据
// public String getPostBodyContent(){
// return getPropertyAsString(postBodyContent);
// }
public Arguments getArguments() {
return (Arguments) getProperty(ARGUMENTS).getObjectValue();
}
public MyPluginSampler(){
setName("sampler练习示例");
classCount.incrementAndGet();
trace("第一个sampler练习");
}
private void trace(String s){
String t1=getTitle();
String tn=Thread.currentThread().getName();
String th=this.toString();
log.debug(tn+" ("+classCount.get()+") "+t1+" "+s+" "+th);
}
@Override
public SampleResult sample(Entry entry) {
trace("sample()");
SampleResult res=new SampleResult();
boolean isOK=false;//did sample is succeed?
String response=null;
//sampler data
String domain=getDomain();
String port=getPort();
String contentEncoding=getContentEncoding();
String path=getPath();
String useKeepAlive=getUseKeepAlive();
String method=getMethod();
// String postBodyContent=getPostBodyContent();
Arguments arguments=getArguments();
JSONObject jo=new JSONObject();
jo.put("domain",domain);
jo.put("port",port);
jo.put("contentEncoding",contentEncoding);
jo.put("path",path);
jo.put("useKeepAlive",useKeepAlive);
jo.put("method",method);
jo.put("arguments",arguments.toString());
res.setSampleLabel(getTitle());
/**
* perform sampler resultData
*/
res.sampleStart();
try{
response=Thread.currentThread().getName();
res.setSamplerData("setSamleData!!!");
res.setResponseData(jo.toString(),null);
log.debug("json:"+jo.toString());
res.setDataType(SampleResult.TEXT);
res.setResponseCodeOK();
res.setResponseMessage("OK");
isOK=true;
}catch (Exception e){
log.debug(" ",e);
res.setResponseCode("500");
res.setResponseMessage(e.toString());
}
res.sampleEnd();
res.setSuccessful(isOK);
return res;
}
}
主要的是override几个方法,实现自己的处理逻辑,现在只是数据串通,后面等明确需求再完善。
4、打成jar包,放入jemeter lib\ext下,启动
5、启动执行看效果
看结果:串通起来了。