posts - 22, comments - 32, trackbacks - 0, articles - 73
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

Spring MVC + Junit4 单元测试

Posted on 2017-07-10 10:17 为自己代言 阅读(451) 评论(0)  编辑  收藏 所属分类: Junit4 单测试

前言:JUnit元数据

@Before: 
使用了该元数据的方法在每个测试方法执行之前都要执行一次。 
@After: 
使用了该元数据的方法在每个测试方法执行之后要执行一次。 
注意:@Before和@After标示的方法只能各有一个。这个相当于取代了JUnit以前版本中的setUp和tearDown方法,当然你还可以继续叫这个名字,不过JUnit不会霸道的要求你这么做了。
@Test(expected=*.class) 
在JUnit4.0之前,对错误的测试,我们只能通过fail来产生一个错误,并在try块里面assertTrue(true)来测试。现在,通过@Test元数据中的expected属性。expected属性的值是一个异常的类型
@Test(timeout=xxx): 
该元数据传入了一个时间(毫秒)给测试方法, 
如果测试方法在制定的时间之内没有运行完,则测试也失败。 
@ignore: 
该元数据标记的测试方法在测试中会被忽略。当测试的方法还没有实现,或者测试的方法已经过时,或者在某种条件下才能测试该方法(比如需要一个数据库联接,而在本地测试的时候,数据库并没有连接),那么使用该标签来标示这个方法。同时,你可以为该标签传递一个String的参数,来表明为什么会忽略这个测试方 法。比如:@lgnore(“该方法还没有实现”),在执行的时候,仅会报告该方法没有实现,而不会运行测试方法。、

一、包含必要地Package

最主要地一个 Package就是org.junit.*,把它包含进来之后,绝大部分功能就有了。还有一句话也非常地重要“import static org.junit.Assert.*;”,我们在测试的时候使用的一系列assertEquals方法就来自这个包。大家注意一下,这是一个静态包含 (static),是JDK5中新增添的一个功能。也就是说,assertEquals是Assert类中的一系列的静态方法

二、测试类的声明

测试类是一个独立的类,没有任何父类。测试类的名字也可以任意命名,没有任何局限性。它与普通类的区别在于它内部的方法的声明

三、创建一个待测试的对象

你要测试哪个类,那么你首先就要创建一个该类的对象。

private staticCalculator calculator =newCalculator();

为了测试Calculator类,我们必须创建一个calculator对象。

四、测试方法的声明

在测试类中,并不是每一个方法都是用于测试的,你必须使用“标注”来明确表明哪些是测试方法。“标注”也是JDK5的一个新特性,用在此处非常恰当。我们可以看到,在某些方法的前有@Before、@Test、@Ignore等字样,这些就是标注,以一个“@”作为开头。这些标注都是JUnit4自定义 的,熟练掌握这些标注的含义非常重要。

六、 忽略测试某些尚未完成的方法

七、 Fixture(暂且翻译为“固定代码段”)

Fixture 的含义就是“在某些阶段必然被调用的代码”。“在任何一个测试执行之前必须执行的代码”就是一个Fixture,我们用@Before来标注它

一、 高级Fixture

两个Fixture标注,分别是@Before和@After,是否适合完成如下功能:有一个类是负责对大文件(超过 500兆)进行读写,他的每一个方法都是对文件进行操作。换句话说,在调用每一个方法之前,我们都要打开一个大文件并读入文件内容,这绝对是一个非常耗费时间的操作。如果我们使用@Before和@After,那么每次测试都要读取一次文件,效率及其低下。这里我们所希望的是在所有测试一开始读一次文件, 所有测试结束之后释放文件,而不是每次测试都读文件。JUnit的作者显然也考虑到了这个问题,它给出了@BeforeClass 和 @AfterClass两个Fixture来帮我们实现这个功能。从名字上就可以看出,用这两个Fixture标注的函数,只在测试用例初始化时执行@BeforeClass方法,当所有测试执行完毕之后,执行@AfterClass进行收尾工作。在这里要注意一下,每个测试类只能有一个方法被标注为 @BeforeClass或@AfterClass,并且该方法必须是Public和Static的。

二、 限时测试

那个求平方根的函数有Bug,是个死循环:

  public voidsquareRoot(intn) ...{

         for(; ;) ;//Bug : 死循环

  }

如果测试的时候遇到死循环,对于那些逻辑很复杂,循环嵌套比较深的程序,很有可能出现死循环,因此一定要采取一些预防措施。我们给这些测试函数设定一个执行时间,超过了这个时间,他们就会被系统强行终止,并且系统还会向你汇报该函数结束的原因是因为超时,这样你就可以发现这些Bug了。只需要给@Test标注加一个参数即可,代码如下:

  @Test(timeout = 1000)

  public voidsquareRoot() ...{

         calculator.squareRoot(4);

         assertEquals(2,calculator.getResult());

  }

  Timeout参数表明了你要设定的时间,单位为毫秒,因此1000就代表1秒。

三、 测试异常

经常会编写一些需要抛出异常的函数,如果一个函数应该抛出异常,但是它没抛出,当然是Bug。例如,我们写的计算器类有除法功能,如果除数是一个0,那么必然要抛出“除0异常”。因此,我们很有必要对这些进行测试。代码如下:

  @Test(expected = ArithmeticException.class)

  public void divideByZero() ...{

         calculator.divide(0);

  }

如上述代码所示,我们需要使用@Test标注的expected属性,将我们要检验的异常传递给他,这样JUnit框架就能自动帮我们检测是否抛出了我们指定的异常。

四、 Runner (运行器)

把测试代码提交给JUnit框架后,框架如何来运行代码呢?答案就是——Runner。在JUnit中有很多个 Runner,他们负责调用测试代码,每一个Runner都有各自的特殊功能,要根据需要选择不同的Runner来运行测试代码。JUnit中有一个默认Runner,如果没有指定,那么系统自动使用默认 Runner来运行你的代码。换句话说,下面两段代码含义是完全一样的:

import org.junit.runner.RunWith;

import org.junit.runners.Suite;

 

@RunWith(Suite.class)

@Suite.SuiteClasses({

MyTestCase.class, //测试类

PartSuite.class, //另一个测试套

})

public class AllTestCases {

}

  要想指定一个Runner,需要使用@RunWith标注,并且把你所指定的Runner作为参数传递给它。另外一个要注意的 是,@RunWith是用来修饰类的,而不是用来修饰函数的。只要对一个类指定了Runner,那么这个类中的所有函数都被这个Runner来调用。

五、 参数化测试

一个对考试分数进行评价的函数,返回值分别为“优秀,良好,一般,及格,不及格”,因此你在编写测试的时候,至少要写5个测试,把这5中情况都包含了,这 确实是一件很麻烦的事情。我们还使用我们先前的例子,测试一下“计算一个数的平方”这个函数,暂且分三类:正数、0、负数。测试代码如下:

importorg.junit.AfterClass;

  importorg.junit.Before;

  importorg.junit.BeforeClass;

  importorg.junit.Test;

  importstatic org.junit.Assert.*;

  public classAdvancedTest ...{

         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提出了“参数化测试”的概念,只写一个测试函数,把这若干种情况作为参数传递进去,一次性的完成测试。代码如下:

  importstatic org.junit.Assert.assertEquals;

  importorg.junit.Test;

  importorg.junit.runner.RunWith;

  importorg.junit.runners.Parameterized;

  importorg.junit.runners.Parameterized.Parameters;

  importjava.util.Arrays;

  importjava.util.Collection;

  @RunWith(Parameterized.class)

  public classSquareTest{

  private static Calculator calculator = new Calculator();

         private int param;

         private int result;

         @Parameters

         public static Collection data(){

                return Arrays.asList(newObject[][]...{

                {2, 4},

                {0, 0},

                {-3, 9},

         });

  }

//构造函数,对变量进行初始化

六、断言和假设

断言:org.junit.Assert用于测试用例中,如果断言失败,用例即结束。

假设:org.junit.Assume用于在准备环境时判断环境是否符合要求,包括测试套的@BeforeClass,测试类的@BeforeClass,测试类的实例化,测试类的@Before。

如果假设失败,假设所处初始化代码方法立即结束,更深级别的后续工作也被忽略,相关测试用例被忽略,但与假设同级别的收尾工作还要继续执行。 
例如:如果在测试类的@BeforeClass中假设失败,该类的实例化及子级别将被忽略,@AfterClass会继续执行。
七、工程实例
     
 如果不想在单元测试中操作数据库中的数据。可以在测试方法上加 
     @Test

    @Transactional //单元测试  @Transactional 不会进行数据提交事物
    @Rollback(true) // 这个注释可以不用加(单元测试默认值)
    public void testaddUserPrivate()throws Exception{
        UserPrivate userPrivate=new UserPrivate();
        userPrivate.setTenantId("31");
        userPrivate.setMenuCode("7777888");
        userPrivate.setpUid("111222");
        userPrivate.setRoleId("3332277");
        userPrivate.setValue("99999");
        userPrivate.setUpdateDateTime(new Date());
        userPrivate.setCreateDateTime(new Date());
        int s=userPrivateService.addUserPrivate(userPrivate);
        Assert.assertEquals(1, s); // 这是断言的使用
    }
        

只有注册用户登录后才能发表评论。


网站导航: