测试的起因是由于业务单号老是重复生成一样,因为多个人同时操作导致的。为了防止这种情况再次发生,在数据库sql里加了行级锁,加了以后需要测试效果,由此有了这次测试。
做junit测试,先在pom.xml中引入junit和spring测试所需要的包依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
建一个多线程类,用来执行具体操作
public class ThreadDemo extends Thread {
@Autowired
private AccVoucherManager accVoucherManager;
public void run(){
try {
System.out.println("-----------------执行前------------------");
accVoucherManager.saveAccVoucher();
System.out.println("-----------------执行后------------------");
} catch (Exception e) {
}
}
}
然后建一个测试类,用来调用多线程类,记得要加上注解:
@RunWith(SpringJUnit4ClassRunner.class):表示使用junit进行测试,
@ContextConfiguration({"…/…/…/spring/spring*.xml"}):表示加载spring配置文件,这两个注解一定要有
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"../../../spring/spring*.xml"})
public class JunitTest {
@Test
public void ThreadTest() {
for (int i = 1; i <=5; i++) {
System.out.println("-----------------开始------------------");
new ThreadDemo().start();
System.out.println("-----------------结束------------------");
}
}
}
然后开始测试,测试结果报空指针
是因为accVoucherManager为空,说明了这个对象没有被spring初始化,但是我在测试类中加了加载spring的配置文件,文件中也扫描了这个类所在的包,为什么没有实例化呢。
带着这个疑问我单独测试了一下多线程类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"../../../spring/spring*.xml"})
public class ThreadDemo extends Thread {
@Autowired
private AccVoucherManager accVoucherManager;
@Test
public void run(){
try {
System.out.println("-----------------执行前------------------");
accVoucherManager.saveAccVoucher();
System.out.println("-----------------执行后------------------");
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果可以调用成功,并没有报空指针,accVoucherManager被实例化了。对比两个类中发现,在进行junit测试的类中写accVoucherManager会被初始化,而在测试类中调用其他类的accVoucherManager,则没有被初始化。说明了在进行springjunit测试的时候,交由spring实例化的对象要在测试类中声明,才能调用。
继续进行测试,知道了要在测试类中声明对象后,重新写了一个测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"../../../spring/spring*.xml"})
public class AccVoucherManagerImpl extends Thread {
@Autowired
private AccVoucherMapper accVoucherMapper;
@Autowired
private CommManager commManager;
@Test
public void threadTest() {
for (int i = 1; i <=5; i++) {
System.out.println("-----------------开始------------------");
new AccVoucherManagerImpl().start();
//saveAccVoucher();
System.out.println("-----------------结束------------------");
}
}
public void run() {
saveAccVoucher();
}
//测试并发调用
@Transactional
public Integer saveAccVoucher() {
AccVoucher accVoucher=new AccVoucher();
try {
if (accVoucher.getVoucherId() == null || accVoucher.getVoucherId() == 0) {
System.out.println("-----------------执行1------------------");
Map<String, Object> keyMap = commManager.getPrimaryKeyNo("SC00330", AccVoucher.TABLE_NAME);
accVoucher.setVoucherId(ConvertUtil.toInt32(keyMap.get("P_KEY_ID")));
accVoucher.setVoucherNo(ConvertUtil.toString(keyMap.get("P_KEY_NO")));
accVoucher.setBusinessNo("SC00330");
accVoucher.setVoucherDate(new Timestamp(new Date().getTime()));
CommDomainUtil2.setBaseInfo(accVoucher);
accVoucher.setRemark("并发测试数据");
accVoucherMapper.insertSelective(accVoucher);
System.out.println("-----------------执行2------------------");
}
} catch (Exception e) {
e.printStackTrace();
}
return accVoucher.getVoucherId();
}
}
测试发现,报空指针,对象写在了测试类里,为什么还有空指针呢。经过排查发现,原来是junit不支持多线程,因此才会报空指针。后来查找资料发现有个第三方的包来支持junit多线程,就是GroboUtils这个包,
GroboUtils官网如下:
http://groboutils.sourceforge.net/ 下载页面:
http://groboutils.sourceforge.net/downloads.html 在pom.xml引入
<dependency>
<groupId>net.sourceforge.groboutils</groupId>
<artifactId>groboutils-core</artifactId>
<version>5</version>
</dependency>
找了个代码样例,放进测试类中,新的测试类如下
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"../../../spring/spring*.xml"})
public class AccVoucherManagerImpl {
@Autowired
private AccVoucherMapper accVoucherMapper;
@Autowired
private CommManager commManager;
@Test
public void MultiRequestsTest() { //junit不支持多线程,故引入第三方插入包
// 构造一个Runner
TestRunnable runner = new TestRunnable() {
@Override
public void runTest() throws Throwable {
saveAccVoucher();
}
};
//Rnner数组,想当于并发多少个。
int runnerCount = 5;
TestRunnable[] trs = new TestRunnable[runnerCount];
for (int i = 0; i < runnerCount; i++) {
trs[i] = runner;
}
//用于执行多线程测试用例的Runner,将前面定义的单个Runner组成的数组传入
MultiThreadedTestRunner mttr = new MultiThreadedTestRunner(trs);
try {
// 开发并发执行数组里定义的内容
mttr.runTestRunnables();
} catch (Throwable e) {
e.printStackTrace();
}
}
//测试并发调用
@Transactional
public Integer saveAccVoucher() {
AccVoucher accVoucher=new AccVoucher();
try {
if (accVoucher.getVoucherId() == null || accVoucher.getVoucherId() == 0) {
System.out.println("-----------------执行1------------------");
Map<String, Object> keyMap = commManager.getPrimaryKeyNo("SC00330", AccVoucher.TABLE_NAME);
accVoucher.setVoucherId(ConvertUtil.toInt32(keyMap.get("P_KEY_ID")));
accVoucher.setVoucherNo(ConvertUtil.toString(keyMap.get("P_KEY_NO")));
accVoucher.setBusinessNo("SC00330");
accVoucher.setVoucherDate(new Timestamp(new Date().getTime()));
CommDomainUtil2.setBaseInfo(accVoucher);
accVoucher.setRemark("并发测试数据");
accVoucherMapper.insertSelective(accVoucher);
System.out.println("-----------------执行2------------------");
}
} catch (Exception e) {
e.printStackTrace();
}
return accVoucher.getVoucherId();
}
}
测试了,发现终于没问题了
最后来总结一下junit测试的心得:
1、引入junit需要的包
2、在测试类中加上注解
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"…/…/…/spring/spring*.xml"})
3、测试多线程的话,要引入第三方包GroboUtils