测试的起因是由于业务单号老是重复生成一样,因为多个人同时操作导致的。为了防止这种情况再次发生,在数据库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("-----------------结束------------------");
        }
	}

}

然后开始测试,测试结果报空指针

maven项目怎么使用线程池_spring


是因为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