在每个单独的功能模块里建测试文件夹写单元测试
springJUNIT用法???
一:eclispe/Myeclipse中已经添加好了junit的jar包,在build path中选择add Library中选择添加junit的jar包即可。如果idea中没有自己下载然后添加即可(在IDEA中添加插件:File-Settings-Plugins,没有可以从仓库中添加)。
二:生成Junit测试框架:
run——edit configration——添加junit,在右侧配置configration:
Test kind:Method
Class:测试类
Method:测试方法名
三:具体执行:
在打开的类中快捷键ctrl,shift+T即可,然后勾选想要测试的方法,勾选几个测试类中便会创建几个。
四:执行规则:
1.必须用@Test注解进行修饰
2.测试方法必须使用public void修饰,且测试方法不能有任何参数。测试逻辑自己写,其实就是创建目标类的实例然后调用其方法。
3.通常用@before注解的方法来初始化
3.测试类的包要与被测试类的包结构一致。
4.测试单元中的各个测试方法不能有依赖,必须独立测试。
设置类变量给类变量赋值创建实例private static ... = new ...(或用@beforeClass来进行实例化)
https://www.cnblogs.com/huaxingtianxia/p/5563111.html
http://blog.csdn.net/andycpp/article/details/1327346
测试本质是创建目标类的实例对实例的方法进行各种操作,必然会改变实例的属性值,对某些实例方法的测试前对实例的属性值有特殊要求,此时便可以使用@before注解的方法来使该属性值变为特定值。
assertEquals用来断言基本类型是否相同
assertSame用来断言对象是否相同
通常单元测试用来测业务层写的方法
junit会遇到spring容器多次初始化的问题吗?
springJUNIT
-
导致多次Spring容器初始化问题
根据JUnit测试方法的调用流程,每执行一个测试方法都会创建一个测试用例的实例并调用setUp()方法。由于一般情况下,我们在setUp()方法 中初始化Spring容器,这意味着如果测试用例有多少个测试方法,Spring容器就会被重复初始化多次。虽然初始化Spring容器的速度并不会太 慢,但由于可能会在Spring容器初始化时执行加载Hibernate映射文件等耗时的操作,如果每执行一个测试方法都必须重复初始化Spring容 器,则对测试性能的影响是不容忽视的;使用Spring测试套件,Spring容器只会初始化一次! -
需要使用硬编码方式手工获取Bean
在测试用例类中我们需要通过ctx.getBean()方法从Spirng容器中获取需要测试的目标Bean,并且还要进行强制类型转换的造型操作。这种乏味的操作迷漫在测试用例的代码中,让人觉得烦琐不堪;使用Spring测试套件,测试用例类中的属性会被自动填充Spring容器的对应Bean,无须在手工设置Bean! -
数据库现场容易遭受破坏
测试方法对数据库的更改操作会持久化到数据库中。虽然是针对开发数据库进行操作,但如果数据操作的影响是持久的,可能会影响到后面的测试行为。举个例子, 用户在测试方法中插入一条ID为1的User记录,第一次运行不会有问题,第二次运行时,就会因为主键冲突而导致测试用例失败。所以应该既能够完成功能逻 辑检查,又能够在测试完成后恢复现场,不会留下“后遗症”;使用Spring测试套件,Spring会在你验证后,自动回滚对数据库的操作,保证数据库的现场不被破坏,因此重复测试不会发生问题! -
不方便对数据操作正确性进行检查
假如我们向登录日志表插入了一条成功登录日志,可是我们却没有对t_login_log表中是否确实添加了一条记录进行检查。一般情况下,我们可能是打开 数据库,肉眼观察 是否插入了相应的记录,但这严重违背了自动测试的原则。试想在测试包括成千上万个数据操作行为的程序时,如何用肉眼进行检查?
只要你继承Spring的测试套件的用例类,你就可以通过jdbcTemplate在同一事务中访问数据库,查询数据的变化,验证操作的正确性!
1. maven 配置
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.12</version>
<scope>test</scope>
</dependency>
2.创建BaseJunit4Test基类
创建 Spring Test 的基类,该类主要用来加载配置文件,设置web环境。
所有的测试类,都继承该类即可。
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringJUnit4ClassRunner.class) //使用junit4进行测试
@ContextConfiguration(locations={"classpath:spring.xml","classpath:spring-mvc.xml","classpath:spring-hibernate.xml","classpath:spring-ehcache.xml"}) //加载配置文件
@WebAppConfiguration("src/main/webapp")
//------------如果加入以下代码,所有继承该类的测试类都会遵循该配置,也可以不加,在测试类的方法上///控制事务,参见下一个实例
//这个非常关键,如果不加入这个注解配置,事务控制就会完全失效!
//@Transactional
//这里的事务关联到配置文件中的事务控制器(transactionManager = "transactionManager"),同时//指定自动回滚(defaultRollback = true)。这样做操作的数据才不会污染数据库!
//@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
//------------
public class BaseJunit4Test{
}
- @RunWith(SpringJUnit4ClassRunner.class) 使用junit4进行测试
- @ContextConfiguration() 加载spring相关的配置文件
- @WebAppConfiguration() 设置web项目的环境,如果是Web项目,必须配置该属性,否则无法获取 web 容器相关的信息(request、context 等信息)
3.测试类
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
import cn.com.infcn.ade.service.UserManagerService;
import cn.com.infcn.model.pmodel.AdeUser;
public class UserTest extends BaseJunit4Test{
@Autowired //自动注入
private UserManagerService userManagerService;
@Test
@Transactional //标明此方法需使用事务
@Rollback(false) //标明使用完此方法后事务不回滚,true时为回滚
public void testUser(){
System.out.println("测试Spring整合Junit4进行单元测试");
AdeUser user = userManagerService.get("0");
System.out.println(user);
System.out.println("------------"+user.getLoginName());
}
}
- 使用Spring Test 可以使用@Autowired 自动注入 相关的bean信息,而不需要自己手动通过getBean去获取相应的bean信息。
- @Transaction
使用Spring Test 测试,可以 @Transaction 注解,表示该方法使用spring的事务。 - @Rollback(false)
标明使用完此方法后事务不回滚,true时为回滚。
比如每次打包或提交时,都执行下所有的测试类,而测试类每次都进行插入或删除数据或导致数据库中的数据不完整,为了防止执行测试类都修改库中的数据,可以设置Rollback(true)。
同事给的
package com.jd.trip.hotel.sip.collection.test.base;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:spring-config.xml", "classpath:net/bull/javamelody/monitoring-spring-aspectj.xml"})
public abstract class BaseTest {
protected Logger logger = LoggerFactory.getLogger(getClass());
}
public class HttpClient {
private AsyncHttpClient client;
private final static int connectTimeout = 5000;
private final static int readTimeout = 10000;
private final static int requestTimeout = 10000;
private static class HttpClientHolder {//懒加载
private static HttpClient httpClient = new HttpClient();
}
private HttpClient() {
AsyncHttpClientConfig.Builder configBuilder = new AsyncHttpClientConfig.Builder();
configBuilder.setConnectTimeout(connectTimeout);
configBuilder.setReadTimeout(readTimeout);
configBuilder.setRequestTimeout(requestTimeout);
this.client = new AsyncHttpClient(configBuilder.build());
}
public static HttpClient getAsyncHttpClient() {
return HttpClientHolder.httpClient;
}
public String get(String url) {
try {
ListenableFuture<Response> futrue = this.client.prepareGet(url).execute();
Response res = futrue.get();
String responseMsg = res.getResponseBody();
return responseMsg;
} catch (Exception e) {
GLogger.error("HttpClient 请求供应商接口--请求失败--请求地址及异常", url.concat("----->").concat(e.getMessage()));
throw new SoaServiceRpcException(ErrorEnum.SIP_HTTP_BAD_UNKNOWN.getCode(), ErrorEnum.SIP_HTTP_BAD_UNKNOWN.getMessage());
}
}
public ListenableFuture<Response> get(String url, AsyncHandler resHandler) {
return this.client.prepareGet(url).execute(resHandler);
}
private Request buildRequest(String url, Map<String, String> paramsMap) {
RequestBuilder requestBuilder = new RequestBuilder();
if (paramsMap != null && paramsMap.size() > 0) {
Set<Map.Entry<String, String>> entrySet = paramsMap.entrySet();
Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
if (entry.getKey() != null) {
requestBuilder.addFormParam(entry.getKey(), entry.getValue());
}
}
}
// 添加RequestHeader,key
requestBuilder.addHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");
requestBuilder.addHeader("Accept", "application/json");
requestBuilder.addHeader("connection", "Keep-Alive");
requestBuilder.addHeader("Content-Length", "0");
requestBuilder.setMethod("POST");
requestBuilder.setUrl(url);
return requestBuilder.build();
}
public ListenableFuture<Response> post(String url, Map<String, String> paramsMap) {
Request req = this.buildRequest(url, paramsMap);
return this.client.executeRequest(req);
}
public void post(String url, Map<String, String> paramsMap, AsyncHandler resHandler) {
Request req = this.buildRequest(url, paramsMap);
this.client.executeRequest(req, resHandler);
}
public void callBack(com.google.common.util.concurrent.ListenableFuture<Response> future, DeferredResult<String> results) {
Futures.addCallback(future, new FutureCallback<Response>() {
@Override
public void onSuccess(Response result) {
String responseMsg = null;
try {
responseMsg = analysisInputStream(result);
results.setResult(responseMsg);
} catch (IOException e) {
results.setErrorResult(e.getMessage());
results.setResult(null);
}
}
@Override
public void onFailure(Throwable t) {
results.setErrorResult(t.getMessage());
}
});
}
public com.google.common.util.concurrent.ListenableFuture<Response> getFuture(ListenableFuture<Response> execute) {
return new com.google.common.util.concurrent.ListenableFuture<Response>() {
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return execute.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return execute.isCancelled();
}
@Override
public boolean isDone() {
return execute.isDone();
}
@Override
public Response get() throws InterruptedException, ExecutionException {
return execute.get();
}
@Override
public Response get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return execute.get(timeout, unit);
}
@Override
public void addListener(Runnable listener, Executor executor) {
execute.addListener(listener, executor);
}
};
}
public static String analysisInputStream(Response input) throws IOException {
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
try {
inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
char[] szBuffer = new char[1024];
int result = inputStreamReader.read(szBuffer);
StringBuffer buffer = new StringBuffer();
while (result != -1) {
buffer.append(new String(szBuffer, 0, result));
result = inputStreamReader.read(szBuffer);
}
return buffer.toString();
} catch (Exception e) {
return input.getResponseBody("UTF-8");
} finally {
if (inputStream != null) {
inputStream.close();
}
if (inputStreamReader != null) {
inputStreamReader.close();
}
}
}
public String post(String url, String param) {
Request req = this.buildRequest(url, param);
return httpPost(req, url);
}
protected String httpPost(Request req, String url) {
try {
ListenableFuture<Response> future = this.client.executeRequest(req);
Response res = future.get(requestTimeout, TimeUnit.MILLISECONDS);
String responseMsg = res.getResponseBody();
return responseMsg;
} catch (TimeoutException e) {
GLogger.logError("AsyncHttpClient 网络请求超时,url:{},e:{}", url, ExceptionUtil.LogExceptionStack(e));
return NetworkErrorEnum.NETWORK_TIME_OUT.getKey();
} catch (Exception e) {
GLogger.logError("AsyncHttpClient 网络异常,url:{}, e:{}", url, ExceptionUtil.LogExceptionStack(e));
return NetworkErrorEnum.NETWORK_ERROR.getKey();
}
}
private Request buildRequest(String url, String params) {
RequestBuilder requestBuilder = new RequestBuilder();
requestBuilder.addHeader("Content-type", "application/json;charset=utf-8");
requestBuilder.addHeader("Accept", "application/json");
requestBuilder.addHeader("connection", "Keep-Alive");
requestBuilder.addHeader("Content-Length", String.valueOf(params.length()));
requestBuilder.setMethod("POST");
requestBuilder.setUrl(url);
requestBuilder.setBody(params);
return requestBuilder.build();
}
}
项目示例:
package com.jd.trip.hotel.sip.ctrip.web.test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath*:spring-config.xml", "classpath:net/bull/javamelody/monitoring-spring-aspectj.xml"}) public abstract class BaseTest { protected Logger logger = LoggerFactory.getLogger(getClass()); }
package com.jd.trip.hotel.sip.ctrip.web.test.resourceTest; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.jd.ihexpedia.common.constant.Constant; import com.jd.ihexpedia.common.http.HttpClient; import com.jd.ihexpedia.common.util.GLogger; import com.jd.ihexpedia.common.util.JsonUtil; import com.jd.ihexpedia.domain.bo.hotel.ExpediaHotelInfoBO; import com.jd.trip.hotel.sip.ctrip.web.test.BaseTest; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.junit.Test; import org.springframework.core.io.ClassPathResource; import sun.text.normalizer.UTF16; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.List; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /** * @description: * @author: libin293 * @requireNo: * @createdate: 2018-06-05 14:59 * @lastdate: 2018/6/5 */ public class HotelInfoTest extends BaseTest{ @Test public void test() { System.out.println("开始执行"); HttpURLConnection conn = null; FileOutputStream out = null; BufferedOutputStream bf = null; int len = -1; try { conn = (HttpURLConnection) new URL(Constant.HOTEL_INFO_URL).openConnection(); HttpURLConnection conn2 = (HttpURLConnection) new URL(Constant.HOTEL_INFO_NAME_MAPPING_URL).openConnection(); buildConn(conn); buildConn(conn2); conn.connect(); conn2.connect(); Map<String, String> hotelNameMap = Maps.newHashMap(); Map<String, String> hotelAddressMap = Maps.newHashMap(); /*hotelName读取*/ if(conn2.getResponseCode() == 200) { ZipInputStream zis2 = new ZipInputStream(conn2.getInputStream()); FileOutputStream hotelName = new FileOutputStream(new File("HotelName.txt")); while ((zis2.getNextEntry()) != null) { while ((len = zis2.read(Constant.readBytes)) > -1) { hotelName.write(Constant.readBytes, 0, len); } } hotelName.flush(); hotelName.close(); zis2.close(); } /*HotelName解析*/ BufferedReader buffer2 = new BufferedReader(new InputStreamReader(new FileInputStream(new File("HotelName.txt")), "UTF-8")); String line1; while ((line1 = buffer2.readLine()) != null) { String [] line1s = line1.split("\\|"); if(line1.contains("EANHotelID") || line1s.length <= 4 || !isChinese(line1s[2])) { /*过滤非中文酒店名称*/ continue; } String hotelId = line1s[0]; String hotelNameCN = line1s[2]; String hotelAddressCN =line1s[3]; hotelNameMap.put(hotelId, hotelNameCN); hotelAddressMap.put(hotelId, hotelAddressCN); } System.out.println("酒店名称解析结果:" + hotelNameMap.size()); /*HotelInfo读取*/ List<ExpediaHotelInfoBO> expediaHotelInfoBOList = Lists.newArrayList(); if (conn.getResponseCode() == 200) { ZipInputStream zis = new ZipInputStream(conn.getInputStream()); FileOutputStream hotelOut = new FileOutputStream(new File("D://HotelInfo.txt")); while ((zis.getNextEntry()) != null) { while ((len = zis.read(Constant.readBytes)) > -1) { hotelOut.write(Constant.readBytes, 0, len); } } hotelOut.flush(); hotelOut.close(); zis.close(); System.out.println("写入文件结束"); } /*HotelInfo解析*/ BufferedReader buffer = new BufferedReader(new InputStreamReader(new FileInputStream(new File("D://HotelInfo.txt")), "UTF-8")); String line; while ((line = buffer.readLine()) != null) { String [] hotelInfoLines = line.split("\\|"); if(StringUtils.isEmpty(hotelInfoLines[8]) || !hotelInfoLines[8].trim().equals("CN")) { //过滤非国内酒店 continue; } ExpediaHotelInfoBO expediaHotelInfoBO = new ExpediaHotelInfoBO(); expediaHotelInfoBO.setHotelCode(hotelInfoLines[0]); expediaHotelInfoBO.setHotelNameEn(hotelInfoLines[2]); expediaHotelInfoBO.setHotelNameCn(hotelNameMap.get(hotelInfoLines[0])); expediaHotelInfoBO.setCityCode(hotelInfoLines[7]); expediaHotelInfoBO.setCityName(hotelInfoLines[5]); expediaHotelInfoBO.setAddress(hotelAddressMap.get(hotelInfoLines[0])); expediaHotelInfoBO.setLatitude(hotelInfoLines[9]); expediaHotelInfoBO.setLongitude(hotelInfoLines[10]); expediaHotelInfoBOList.add(expediaHotelInfoBO); } System.out.println("执行结束, 结果:" + JsonUtil.vo2JsonString(expediaHotelInfoBOList.get(0)) + "总数:" + expediaHotelInfoBOList.size()); } catch (Exception e) { GLogger.logError("Expedia-getNativeHotelInfo-下载文件出错e:{}", ExceptionUtils.getStackTrace(e)); } finally { if (conn != null) { conn.disconnect(); } if (out != null) { try { out.close(); } catch (Exception e) { GLogger.logError("close FileOutputStream Exception as:{}", ExceptionUtils.getStackTrace(e)); } } } } public void updateNativeHotelInfo() { } // 判断一个字符串是否含有中文 public static boolean isChinese(String str) { if (str == null) return false; for (char c : str.toCharArray()) { if (isChinese(c)) return true;// 有一个中文字符就返回 } return false; } public static boolean isChinese(char c) { return c >= 0x4E00 && c <= 0x9FA5;// 根据字节码判断 } public static void buildConn(HttpURLConnection connection) throws Exception{ connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); connection.setRequestMethod("GET"); connection.setRequestProperty("Accept-Encoding", "gzip, deflate"); connection.setRequestProperty("Connection", "keep-alive"); connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36"); } public List<ExpediaHotelInfoBO> readHotelInfoFromTxt(String fileName) throws Exception{ List<ExpediaHotelInfoBO> expediaHotelInfoBOList = Lists.newArrayList(); InputStream in = new ClassPathResource(fileName).getInputStream(); BufferedReader buffer = new BufferedReader(new InputStreamReader(in, "UTF-8")); String line; while ((line = buffer.readLine()) != null) { String [] hotelInfoLines = line.trim().split("\\|"); ExpediaHotelInfoBO expediaHotelInfoBO = new ExpediaHotelInfoBO(); expediaHotelInfoBO.setHotelCode(hotelInfoLines[0]); expediaHotelInfoBO.setHotelNameEn(hotelInfoLines[2]); expediaHotelInfoBO.setCityCode(hotelInfoLines[7]); expediaHotelInfoBO.setCityName(hotelInfoLines[5]); expediaHotelInfoBO.setAddress("".concat(hotelInfoLines[3]).concat(hotelInfoLines[4])); expediaHotelInfoBO.setLatitude(hotelInfoLines[9]); expediaHotelInfoBO.setLongitude(hotelInfoLines[10]); expediaHotelInfoBOList.add(expediaHotelInfoBO); } return expediaHotelInfoBOList; } }