Junit4单元测试

Junit4单元测试

官方文档

首先局部 用法

1.1 常见效用

典型配置:

/*用于配置spring Boot中测试的环境*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MyBlogApplication.class)
/* 开启事务,测试完成默认自动回滚,不会弄脏数据库 */
@Transactional

public class WhoHaveWhatTagsMapperTest {

    @BeforeClass
    public static void beforeClass() {
    }

    @Before
    public void setUp() throws Exception {
    }

    @After
    public void tearDown() throws Exception {
    }

    @Test
    public void insertWhoHaveWhatTags() throws Exception {
    }

    @Test
    public void selectBlogByTag() throws Exception {
    }

    @Test
    public void deleteWhoHaveWhatTags() throws Exception {
    }

}
  • @Test:把一个法标记为测试方法
    片独特性:
    excepted;表示测试在实施中希抛来的不行类型,如若无丢来,反而报错。
    timeout:超时抛来好。单位飞秒

    @Test(timeout = 2000)
    @Test(expected = Exception.class)
    public void testFactorialException() throws Exception {
        new Math().factorial(-1);
        fail("factorial参数为负数没有抛出异常");
    }
    
  • @Before:每一个测试方法执行前自行调用一坏

  • @After:每一个测试方法执行了自动调用两回
  • @BeforeClass:所有测试方法执行前履行同一破,在测试类还不曾实例化就曾给加载,所以用static修饰
  • @AfterClass:所有测试方法执行了执行同样不良,在测试接近还尚无实例化就早已于加载,所以用static修饰
  • @Ignore:暂无执该测试方法

  • setup方法要实现测试前之伊始化工作

  • teardown方法重要实现测试成功后垃圾回收工作!

setup方法首要实现测试前之起头化工作,teardown方法要实现测试就后舍弃物回收工作!
测试方法的宣示要求:名字可以随便取,没有此外限制,可是回到值必须为void,而且无可以发生外参数。

  • 参数化测试
    若或际遇过如此的函数,它的参数有众多奇异值,或者说他的参数分为两只区域。比如测试一下“总结一个往往的平方”那些函数,暂且分三类:正数、0、负数。测试代码如下:

    public class CalculatorTest {
    private static Calculator calculator = new Calculator();//这个类要自己写
    
    @Before
    public void clearCalculator() {
        calculator.clear();
    }
    @Test
    public void square1() {
        calculator.square(2);
        assertEquals(4, calculator.getResult());
    }
    @Test
    public void square2() {
        calculator.square(0);
        assertEquals(0, calculator.getResult());
    }
    @Test
    public void square3() {
        calculator.square(-3);
        assertEquals(9, calculator.getResult());
    }
    }
    

    为简化类似的测试,JUnit4指出了“参数化测试”的概念,只写一个测试函数,把这多种植情状作参数传递进去,两次性的成就测试。代码如下:

    @RunWith(Parameterized.class)
    public class SquareTest {
    private static Calculator calculator = new Calculator();
    private int param;
    private int result;
    
    @Parameters
        //输入的参数和预期的结果
    public static Collection data() {
        return Arrays.asList(new Object[][] { { 2, 4 }, { 0, 0 }, { -3, 9 }, });
    }
    // 构造函数,对变量进行初始化
    public SquareTest(int param, int result) {
        this.param = param;
        this.result = result;
    }
    @Test
    public void square() {
        calculator.square(param);
        assertEquals(result, calculator.getResult());
    }
    }
    
  • 装进测试
    于一个色面临,只写一个测试类是未可能的,我们会写起多丛单测试类。不过那个测试类必须一个一个之实施,也是较费劲的作业。鉴于此,JUnit也咱提供了包装测试的效用,将富有需要周转的测试类集中起来,一回性的运作了,大大的惠及了俺们的测试工作。具体代码如下:

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

@RunWith(Suite.class)
@Suite.SuiteClasses({CalculatorTest.class, SquareTest.class})
public class AllCalculatorTests{}

自者能够看出,这些意义也欲使用一个破例的Runner,由此咱们要往@RunWith标明传递一个参数Suite.class。同时,大家还得此外一个标明@Suite(SuiteClasses),来注脚那么些仿佛是一个装进测试类。我们管用打包的好像作为参数传递给该标注就好了。有矣即点儿只号之后,就已完全的揭橥了拥有的意义,由此下的类都不值一提,随便起一个类名,内容总体吗空既不过。

  • Assume
    对待测法的参数举行合法性校验的,要是校验不沾边则直放任大,而不举办测试。
    Assume提供的校验规则如下:

      assumeTrue/assumeFalse、 assumeNotNull、 assumeThat、 assumeNoException 
    

    如:(通过下述代码也可看出,要运参数,则答应下@Theory注解)

    @Theory
    public void printAge(String name, int age){
        Assume.assumeTrue(age > 0);//如果参数age<=0,会抛AssumptionViolatedException异常
        System.out.println(String.format("%s's Name is %s.", name, age));
    }
    
  • Assert
    是Junit提供的断言,与Assume不同,Assert是本着测试结果的校验,它提供的查看规则如下:
    AssertTrue、AssertFalse:结果的true、false。
    AssertThat:使用Matcher做打定义之校验。
    AssertEquals、AssertNotEquals:判断七个目的是不是当。
    AssertNull、AssertNotNull:判断目的是不是也空。
    Assert山姆e:判断四只目的是不是为与一个,不同为equals这里是应用“==”判断。
    AssertArrayEquals:判断两独数组是否等。

  • 大抵线程测试
    JUnit4的Test写好将来,对于有集成度相比较大的测试用例,还盼望就并发访问情况下的测试,但是,JUnit4不够省气象并未供,可以通过友好写一个main函数,然后成立几单线程,在五只线程中又运行测试用例举行测试,来法并发访问的状,具体事例:

public class TestExample {

    @Test
    public void testMethod() {
    System.out.println("test success!");
    }
}

public class PerfomanceTest {
    public static void main(String[] args) {
        new Thread() {
            public void run() { 
                // JUnitCore.runClasses(new Class[] { TestExample.class });           (1)
                // new JUnitCore().run(Request.method(TestExample.class, "testMethod"));        (2)
            }
        }.start();
    }
}

流淌:标志1依旧标志2受到使用同一种就好测试。

1.2 Spring的@Transactional注用法

参考:http://www.cnblogs.com/yepei/p/4716112.html
事务管理对于集团应用来说是生死攸关的,尽管出现非常意况,它为可以保证数据的一致性。
Spring Framework对事务管理提供了同样的泛,其特点如下:

  • 也不同的事务API提供相同的编程模型,比如JTA(Java Transaction API),
    JDBC, Hibernate, JPA(Java Persistence API以及JDO(Java Data Objects)
  • 襄助注脚式事务管理,特别是冲声明的注脚式事务管理,简单容易用
  • 提供于此外事务API如JTA更简短的编程式事务管理API
  • 和spring数据看抽象的到集成

事务管理情势
spring帮助编程式事务管理和阐明式事务管理两栽方法。
编程式事务管理使用TransactionTemplate或者直接下底层的PlatformTransactionManager。对于编程式事务管理,spring推荐用TransactionTemplate。

讲明式事务管理建立以AOP之上的。其实质是针对章程前后开展拦,然后于靶措施先河从前创造或者参与一个事务,在推行了目的措施之后因实施情况提交或者回滚事务。注明式事务最深的优点就是是免需经过编程的计管理业务,这样便无欲在事情逻辑代码中混合事务管理的代码,只需要于配备文件被举办连锁的事情规则注脚(或通过按照@Transactional注的点子),便好用业务规则以至业务逻辑中。

分明表明式事务管理要优化编程式事务管理,这正是spring倡导的非侵入式的开发模式。注解式事务管理使业务代码不深受污染,一个一般性的POJO对象,只要添加阐明就可以拿走了的工作扶助。和编程式事务相相比较,申明式事务唯一不足地点是,后者的无比缜密粒度只可以功效及方法级别,无法成功像编程式事务这样可以效率及代码片级别。不过就有这么的需,也存诸多变迁之法,比如,可以拿需要举办事务管理的代码块独立为道等等。

阐明式事务管理也来三三两两栽常用的方法,一种植是基于tx和aop名字空间的xml配置文件,另一样栽不畏是基于@Transactional讲明。显明基于评释的主意又简便易行好用,更舒畅。

默认意况下,数据库处于活动提交格局。每一样修告词处于一个单身的事务中,在当时漫漫告句执行完毕时,倘使进行成功则隐式的交由业务,假诺
实施破产则隐式的回滚事务。

对正规的事务管理,是一样组有关的操作处于一个作业间,因而须关闭数据库的活动提交格局。然而,spring会将根连接的自发性提交特性设置也false。
一连关闭时默认的策略是扭曲滚任何不提交的政工

MyBatis自动插手届spring事务管理中,无需额外部署,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起效用。

spring事务特性

spring所部分事务管理策略类都累自org.springframework.transaction.PlatformTransactionManager接口

public interface PlatformTransactionManager {

  TransactionStatus getTransaction(TransactionDefinition definition)
    throws TransactionException;

  void commit(TransactionStatus status) throws TransactionException;

  void rollback(TransactionStatus status) throws TransactionException;
}

内部TransactionDefinition接口定义以下特征:

作业隔离级别

断级别是看重多少单冒出的事体中的隔断程度。TransactionDefinition
接口中定义了五独象征隔离级另外常量:
•TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示用底层数据库的默认隔离级别。对绝大多数数据库而言,通常就值就是是TransactionDefinition.ISOLATION_READ_COMMITTED。
•TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个政工可以读取另一个政工修改只是还不曾交到的多寡。该级别不克防止污染读,不可再读与幻读,由此特别少用该隔离级别。比如PostgreSQL实际上并不曾此级别。
•TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个业务只好读取另一个事务都交由的数据。该级别可以避免污染读,这也是大部分境况下的推荐值。
•TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在全方位过程中可以多次重复执行有查询,并且每一遍回去的记录还同一。该级别可以防范脏读和不得再读。
•TransactionDefinition.ISOLATION_SERIALIZABLE:所有的作业逐项逐个执行,这样工作中便了无可能有苦恼,也就是说,该级别可以避免水污染读、不可再读与幻读。然则就将严重影响程序的性。平日状态下也不会见因而到该级别。

业务传播行为

所谓事务之流传行为是指,如若超过导时作业以前,一个业务上下文已经存在,此时发出几多摘可以指定一个事务性方法的施行行为。在TransactionDefinition定义着连了之类几个代表传播行为的常量:
•TransactionDefinition.PROPAGATION_REQUIRED:即便手上在业务,则插足该工作;如若手上莫事情,则创建一个初的事情。这是默认值。
•TransactionDefinition.PROPAGATION_REQUIRES_NEW:创设一个初的工作,倘若手上在工作,则将当前事情挂于。
•TransactionDefinition.PROPAGATION_SUPPORTS:如若手上留存工作,则投入该事情;即便手上没有事情,则坐非事务的方延续运行。
•TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务格局运行,假设手上是业务,则拿当下业务挂于。
•TransactionDefinition.PROPAGATION_NEVER:以非事务格局运行,尽管手上在工作,则抛来非常。
•TransactionDefinition.PROPAGATION_MANDATORY:假设手上存业务,则参与该工作;假若手上未曾事情,则委来好。
•TransactionDefinition.PROPAGATION_NESTED:假若手上有业务,则创建一个事务作为当下事情的嵌套事务来运行;即便手上从未有过事情,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

作业超时

所谓事务超时,就是赖一个事务所允许实施之顶丰硕时,假使跨越该时间限制只是工作还一向糟糕,则自动回滚事务。在
TransactionDefinition 中为 int 的价值来代表过时间,其单位凡秒。
默认设置为根工作系统的超时值,假设底层数据库事务系统并未装超时值,那么就是是none,没有过限制。

政工只念属性

但念事务用于客户代码只读而无改数据的状况,只念事务用于特定情景下的优化,比如以Hibernate的时光。
默认为念写作业。

spring事务回滚规则

指令spring事务管理器回滚一个政工之引荐格局是当当前工作的左右文内抛来非凡。spring事务管理器会捕捉任何不处理的雅,然后按照规则决定是否回滚抛来好的事情。

默认配置下,spring唯有以抛出的坏与否运行时unchecked分外时才转滚该工作,也即是废来底异常与否RuntimeException的子类(Errors也会见造成工作回滚),而弃来checked异常则不会见导致业务回滚。
可肯定的配备当抛出这多少个大时回滚事务,包括checked非常。也可以显著概念这多少个很抛来时无回滚事务。

还可编程性的经setRollbackOnly()方法来提示一个工作必须回滚,在调用完setRollbackOnly()后而所能履行之绝无仅有操作就回滚。

@Transactional注解

属性 类型 描述
value String 可选的限定描述符,指定使用的事务管理器
propagation enum: Propagation 可选的事务传播行为设置
isolation enum: Isolation 可选的事务隔离级别设置
readOnly boolean 读写或只读事务,默认读写
timeout int (in seconds granularity) 事务超时时间设置
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组
noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组

@Transactional
可以效率为接口、接口方法、类和近似措施上。当效能为类及时不时,该类的装有
public
方法以还备该种的政工属性,同时,大家吧堪在方级别下该标注来覆盖类级另外定义。
虽然 @Transactional 表明可以效用被接口、接口方法、类以及近似情势及,不过Spring
提出不用在接口或者接口方法齐应用该注解,因为立即唯有以行使基于接口的代办时它们才会生效。此外,
@Transactional 声明应该仅仅让应用至 public 方法齐,这是出于 Spring AOP
的本质决定的。如果您于 protected、private 或者默认同见性的主意齐动
@Transactional 表明,这将受忽视,也非会师废弃来另外特别。

仲有 快速开

2.1 在intellij idea中高速变动测试代码

  1. 拿鼠标放到类的任意地点,摁下Ctrl+Shift+T,然后Create a new Test即可。
    图片 1

老三部分 原理

JUnit4为确保每个测试方法都是单元测试,是独立的互不影响。所以每个测试方法执行前都会晤再一次实例化测试类。

3.1 为什么Junit没有main()方法就是可以运行

Junit4可以一向运行我们的某某方法,没有main入口函数是纯属不行的。其实在org.junit.runner包下,有个JUnitCore.class,其中便发一个
标准的main方法,那虽是JUnit入口函数。

Runner只是一个抽象类,表示用于周转Junit测试用例的家伙,通过它好运行测试并通知Notifier运行的结果。经常大家可以于待测方法所在的切近以上使用@RunWith诠释来啊之测试类指定一个特定的Runner。Junit的默认Runnner——BlockJunit4ClassRunner。当我们无也测试接近添加@RunWith注的时光,其实采纳的饶是是Runner,它看作默认Runner只吗我们提供了着力的基于Junit生命周期的测试表明。下边列有部分较实用之Runner。

  1. Suit——它可以同样不成好执行到在多独八九不离十吃之测试用例,例如:

    @RunWith(Suite.class)
    @SuiteClasses({Person.class, People.class})
    public class TestSuitMain{
      //虽然这个类是空的,但依然可以运行Junit测试,运行时,它会将Person.class和//People.class中的所有测试用命都执行一遍!
    }
    
  2. Parameterized——在一般的单元测试中叫@Test注明标注的测试方法只会是public
    void的,且非可以生出其他输入参数。而这时候常会受咱造成麻烦,因为偶然大家用为测试方法输入参数,甚至是批量指定两个待测参数。这时Parameterized这个Runner就可以满足大家的渴求,用法如下:

    @RunWith(Parameterized.class)
    public class TestGenerateParams{
    private String greeting;
    public TestGenerateParams(String greeting){
        super();
        this.greeting = greeting;
    }
    @Test
    public void testParams(){       
        System.out.println(greeting);
    }
    
    /**
     * 这里的返回的应该是一个可迭代数组,且方法必须是public static
     * @return
     */
    @Parameters
    public static List getParams(){
        return Arrays.asList(new String[][]{{"hello"},{"hi"},{"good morning"},{"how are you"}});
    }
    }
    
  3. Theories——提供平等组参数的排列组合值作为待测方法的输入参数。同时注意到以运用Theories那么些Runner的当儿,我们的待测方法可享输入参数,而立当另的Runner中的测试方法是免成为的。下面是一个例证:

    @RunWith(Theories.class)public class TheoriesTest{
    @DataPoint
    public static String nameValue1 = "Tony";
    @DataPoint
    public static String nameValue2 = "Jim";
    @DataPoint    public static int ageValue1 = 10;
    @DataPoint
    public static int ageValue2 = 20;
    @Theory
    public void testMethod(String name, int age){
        System.out.println(String.format("%s's age is %s", name, age));
    }
    }
    

地点的代码的意是,将”Tony”、”吉姆(Jim)”、10、20季只参数为项目合法的排列组合传于得没办法。因此输出的结果一定也有2×2=4栽:

    Tony's age is 10 

    Tony's age is 20 

    Jim's age is 10 

    Jim's age is 20 

唯独,为了简单,我们除了可采用@DataPoint诠释来提供参数之外,还足以经过@DataPoints注来提供参数,参照上述代码,只待将@DataPoint注标注的季只字段参数替换为如下的少数独即可:

@DataPoints
public static String[] names = {"Tony", "Jim"};
@DataPoints
public static int[] ageValue1 = {10, 20};

3.2 基本过程

率先肯定概念:
1.TestCase
意味着一个测试用例,每一个TestCase实例都对应一个测试,那些测试通过那多少个TestCase实例的名标志,以便在测试结果受指明哪个测试出现了问题。
即每个@Test注的办法分别实例化,而未每个@RunWith注脚的近乎

2.TestSuite
意味着待测试的均等组测试用例。

3.TestFixtrue
TestFixtrue代表一个测试环境。它用来组合一样组测试用例,这组测试用例需要共同的测试运行环境。

过程:

起先化阶段(创设 Testcase 及 TestSuite)
先是创立一个 TestRunner 实例

public static void main (String[] args) {
  junit.textui.TestRunner.run (suite());
 }

然后,构造TestSuite:
TestSuite 选取了Composite 设计情势。在拖欠情势下,可以以 TestSuite
比作一株树,树被好蕴涵子树(其余 TestSuite),也得分包叶子
(TestCase),以此为下递归,直到底层全部实现到叶子结束。
然后拿用测试的类(class文件)当参数传入TestSuite() 方法,
TestSuite(Class theclass) 方法吧 TestSuite 类的构造方法,它亦可自行分析
theclass
所描述的接近的中间有咋样方法要测试,并应用反射转发为TestCase对象(注意每一个TestCase都是用测试类的相同不行再实例化,故相免影响,即:一个TestCase类中好定义很多test方法,但一个TestCase实例只针对许一个测试方法。),参与到新协会之
TestSuite 中。

运转等(运行有的TestCase
针对 TestSuite 中的全部“树结构”递归遍历运行中的节点和叶子。

结果捕捉阶段
运作测试的结果于TestResult实例中记录,所以我们抛出Assert中的老大时,不相会潜移默化下的测试继续运行。

3.3 Spring测试框架+junit4单元测试原理

Spring的重中之重测试框架的中央是TestContext,TestContextManager,TestExcutionListener接口,我们每一遍启动测试的上都碰面创TestContextManager,它其实是管理了一个TestContext来负责持有一个手上测试的上下文,可以实现测试实例的依注入。TestContextManager还负责在测试着改进TestContext的状态并摄及TestExecutionListener,它是为此来监督实际的施行(如靠注入,管理实务等等)。

@RunWith(SpringJUnit4ClassRunner.class)  //使用junit4进行测试  
@ContextConfiguration   ({"/spring/app*.xml","/spring/service/app*.xml"}) //加载配置文件  
@Transactional  
...

季有 常见问题

4.1 同一个测试类内部仍然不同测试类之间的@Test进行各种

JUnit4.11后提供了MethodSorters,在测试类及加注@FixMethodOrder(value)可以起三栽方法对test执行顺序举行点名,如下:
默认(MethodSorters.DEFAULT),按道名(MethodSorters.NAME_ASCENDING)和JVM(MethodSorters.JVM)

  • 默认顺序由方名hashcode值来支配,虽然hash值大小相同,则遵照名的字典顺序确定,不同操作系统可能顺序不同;
  • 遵照章程名称的进展排序,由于是依据字符的字典顺序,所以以这种方法指定执行顺序会始终保持一致;
    不了这种办法欲针对测试方法有早晚之命名规则,如
    测试方法均因testNNN起先(NNN表示测试方法连串号 001-999)
    单元测试的目标就是是测试最好小单位之对,隔离和其它一些的关系,自然吧未可以发出指,不然,一定测试通不了,你不可以知晓是单元中的问题,仍旧外部环境的题材。所以我们只有以blog表的测试着接纳了这种排序规则
  • 按JVM重返的艺术名的次第执行,此种办法下测试方法的施行顺序是不足预测的,即每一回运行的相继可能还不等同(JDK7里更如此).

事实上
Junit里是通过反射机制得到有Junit里之具备测试方法,并转一个艺术的高频组,然后挨家挨户执行数组里之那多少个测试方法;
若果当用annotation指定了行各种,Junit在取测试方法的数组后,会因指定的次第对数组里之方法举办排序;

4.2 不同的测试类之间有重新的操作,怎么样保证测试数据不互相影响

由于Junit4今非昔比测试(即每一个@Test犹是一个独立的单元测试,每个测试方法执行前都会晤再次实例化测试类)的默认执行顺序是随措施名之hash值排序,没有互相测试。
因而能够用@Transactional
阐明每个测试类,测试类内部如若无装工作,则默认和类相同。那么在测试中,只要大家无提交业务,Spring默认会测试截止回滚,由此不同的测试单元往日数据互不影响。

特别注意:在test丁,Spring默认测试停止就会见回滚,假使未思回滚,可以用@Rollback(false)注解;
尽管以相似的Java类吃,Spring默认只有当抛出的不可开交与否运行时unchecked分外时才回滚该事情,也虽然是废来之非常与否RuntimeException的子类(Errors也会导致事情回滚),而弃来checked相当则非晤面招致业务回滚,我们得就此@Transactional注解的rollbackFor属性设置任何的

4.3 DAO层的测试一般insert在极端前面,delete在最终,不同之测试单元内数据要互相利用,怎么收拾?

解决1(不推荐):利用@FixMethodOrder(MethodSorters.NAME_ASCENDING)注脚设定按照章程名字典顺序执行测试,可以随上面的命名情势:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MyBlogApplication.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class BlogMapperTest1 {
    @Autowired
    private BlogMapper blogMapper;

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test1InsertBlog() throws Exception {

    }

    @Test
    public void test2SelectBlogByUserUuid() throws Exception {

    }

    @Test
    public void test3DeleteBlogByBlogUuid() throws Exception {
    }
}

解决2:
每个单元测试都再也社团数据。。。当增删改查很多时不时,为了保证测试类的明通晓白,推荐这种措施。

解决3:
把你待共享数据有操作放到一个@Test注的措施吃,比较适合操作相比少的测试。

网站地图xml地图