初步认识JUnit
目前大多数的基于Java的企业应用软件,肯定少不了单元测试,程序员通过编写单元测试来验证自己程序的有效性;管理者通过持续自动的执行单元测试和分析单元测试覆盖率来确保软件本身的质量。可以说单元测试和集成测试在软件开发整个流程中占有举足轻重的地位。
单元测试,是指对软件中的最小可测试单元进行检查和验证。单元测试不是为了证明程序是对的,而是证明程序没有错。Java常用的单元测试框架有JUnit,TestNG,还有些MOCK框架,这里我们只来讲述JUnit。
JUnit的两种版本是JUnit 3.8和JUnit 4,前者使用反射,后者使用反射和注解。
1 package com.shop.web.test; 2 public class Calculator { 3 private static double result = 0.0; 4 public void add(double num) { 5 result = result + num; 6 } 7 public void substract(double num) { 8 result = result - num; 9 } 10 public void multiply(double num) { 11 result = result * num; 12 } 13 public void divide(double num) { 14 if (num != 0) { 15 result = result / num; 16 } else { 17 result = result; 18 } 19 } 20 // 清零 21 public void clear() { 22 result = 0; 23 } 24 public double getResult() { 25 return result; 26 } 27 }
CalculatorTest.java
1 package com.shop.web.test; 2 import junit.framework.TestCase; 3 public class CalculatorTest extends TestCase { 4 private static Calculator calculator = new Calculator(); 5 6 @Override 7 protected void setUp() throws Exception { 8 System.out.println("JUnit initialize the fixture state by overriding setup "); 9 calculator.clear(); 10 } 11 @Override 12 protected void tearDown() throws Exception { 13 System.out.println("JUnit clean-up after a test by overriding tearDown "); 14 calculator.clear(); 15 } 16 public void testAdd() { 17 System.out.println("add result:" + calculator.getResult()); 18 calculator.add(10.1); 19 assertEquals(10.1, calculator.getResult()); 20 } 21 public void testSubstract() { 22 System.out.println("substract result:" + calculator.getResult()); 23 calculator.add(10.1); 24 calculator.substract(2); 25 assertEquals(8.1, calculator.getResult()); 26 } 27 public void testMultiply() { 28 System.out.println("multiply result:" + calculator.getResult()); 29 calculator.add(12); 30 calculator.multiply(12); 31 assertEquals(144.0, calculator.getResult()); 32 } 33 public void testDivide() { 34 System.out.println("divide result:" + calculator.getResult()); 35 calculator.add(12); 36 calculator.divide(12); 37 assertEquals(1.0, calculator.getResult()); 38 } 39 }
绿条代表程序没有错误
1 public abstract class TestCase extends Assert implements Test 2 A test case defines the fixture to run multiple tests. 3 To define a test case 4 1) implement a subclass of TestCase 5 2) define instance variables that store the state of the fixture 6 3) initialize the fixture state by overriding setUp 7 4) clean-up after a test by overriding tearDown. 8 Each test runs in its own fixture so there can be no side effects among test runs
1 JUnit initialize the fixture state by overriding setup 2 add result:0.0 3 JUnit clean-up after a test by overriding tearDown 4 JUnit initialize the fixture state by overriding setup 5 substract result:0.0 6 JUnit clean-up after a test by overriding tearDown 7 JUnit initialize the fixture state by overriding setup 8 multiply result:0.0 9 JUnit clean-up after a test by overriding tearDown 10 JUnit initialize the fixture state by overriding setup 11 divide result:0.0 12 JUnit clean-up after a test by overriding tearDown
1 public class CalculatorTest4 { 2 private static Calculator calculator = new Calculator(); 3 4 @Before 5 public void setUp() throws Exception { 6 System.out.println("JUnit initialize the fixture state by overriding setup "); 7 calculator.clear(); 8 } 9 @After 10 public void tearDown() throws Exception { 11 System.out.println("JUnit clean-up after a test by overriding tearDown "); 12 calculator.clear(); 13 } 14 @Test 15 public void add() { 16 System.out.println("add result:" + calculator.getResult()); 17 calculator.add(10.1); 18 assertEquals(10.1, calculator.getResult()); 19 }
4、@Test(expected=*.class)
在JUnit4.0之前,对错误的测试,我们只能通过fail来产生一个错误,并在try块里面assertTrue(true)来测试。现在,通过@Test元数据中的expected属性。expected属性的值是一个异常的类型,用来检查抛出预期异常。
1 @Test(expected=ArithmeticException.class) 2 public void divide(){ 3 int i = 2/0; 4 }
5、@Test(timeout=xxx):
该元数据传入了一个时间(毫秒)给测试方法,
如果测试方法在制定的时间之内没有运行完,则测试也失败。
@Test(timeout=1) public void count(){ for (int i = 0; i < 1000000000; i++) { System.out.println(i); } }
该元数据标记的测试方法在测试中会被忽略。当测试的方法还没有实现,或者测试的方法已经过时,或者在某种条件下才能测试该方法(比如需要一个数据库联接, 而在本地测试的时候,数据库并没有连接),那么使用该标签来标示这个方法。同时,你可以为该标签传递一个String的参数,来表明为什么会忽略这个测试 方法。比如:@lgnore(“该方法还没有实现”),在执行的时候,仅会报告该方法没有实现,而不会运行测试方法。
1 @Ignore("此方法现在不需要") 2 @Test 3 public void ignore(){ 4 System.out.println("不需要"); 5 }
Spring整合JUnit
Junit测试Spring可以很方便的进行。
用到jar包:spring-test-xxx.jar,junit4的jar。
需要注解 @RunWith、@ContextConfiguration
@RunWith如: @RunWith(SpringJUnit4ClassRunner.class) //表示继承了SpringJUnit4ClassRunner
@ContextConfiguration如: 用来加载Spring配置文件,@ContextConfiguration(locations = {"classpath:applicationContext-mybatis.xml",……"})
注意:(1)如果spring配置文件applicationContext.xml在classpath路径下,即通常的src目录下,这样加载配置文件,用classpath前缀。
(2)但是在web项目中,有些人喜欢把spring配置文件applicationContext.xml放在WEB-INF目录下,这里不是classpath目录。这种情况可以按如下方式配置:用file前缀,指定配置文件的绝对路径。貌似这种方式不是很友好。 如:locations = { "file:D:\\workspace\\webproxy\\src\\main\\resources\\" + "applicationContext.xml" }
完整代码如下:
package com.shop.web.test; import java.util.Date; import java.util.List; import java.util.UUID; import javax.annotation.Resource; import org.apache.log4j.Logger; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; import com.alibaba.fastjson.JSON; import com.shop.web.entity.ShopUser; import com.shop.web.service.ShopUserService; import com.shop.web.util.DateUtil; /** * service、dao层的测试类 * @author ces * */ @RunWith(SpringJUnit4ClassRunner.class)//表示继承了SpringJUnit4ClassRunner类 @ContextConfiguration(locations = {"classpath:applicationContext-mybatis.xml","classpath:applicationContext-service.xml","classpath:applicationContext-transaction.xml"}) public class ShopControllerTest { private static Logger logger = Logger.getLogger(ShopControllerTest.class); @Resource private ShopUserService shopUserService; @Transactional @Test public void getShopUserById(){ ShopUser shopUser = new ShopUser(); shopUser.setUserid(6); shopUser.setUsername("zhangsan"); shopUser.setPassword("333"); shopUser.setCreateTime(Long.parseLong(DateUtil.getString(new Date(), DateUtil.YMDHMS))); try { shopUserService.insertSelective(shopUser); int i = 2/0; } catch (Exception e) { e.printStackTrace(); logger.info("ShopControllerTest" + e); } logger.info(JSON.toJSONString("*********")); } }