精华内容
参与话题
问答
  • Junit单元测试框架是Java程序开发必备的测试利器,现在最常用的就是Junit4了,在Junit4中所有的测试用例都使用了注解的形式,这比Junit3更加灵活与方便。之前在公司的关于单元测试的培训课程中,讲师仅仅讲述了Junit...

           Junit单元测试框架是Java程序开发必备的测试利器,现在最常用的就是Junit4了,在Junit4中所有的测试用例都使用了注解的形式,这比Junit3更加灵活与方便。之前在公司的关于单元测试的培训课程中,讲师仅仅讲述了Junit4的基本的与生命周期相关的注解的使用,主要包括@BeforeClass、@Before、@Test、@After、@AfterClass这些注解,这些在应付普通简单的单元测试已经足够,然而有很多更加复杂且也会经常遇到的测试需求依靠这些生命周期注解并不能完成!因此这篇分享将为您呈现Junit4的另一片新大陆,且看陈述…

           其实,在单元测试培训课程中,讲师并没有讲到Junit4的核心,例如为什么Junit没有main()方法就能运行(因为我们知道无论是什么程序都必须得有一个程序入口,而它通常是main);在例如Junit的核心组成部分是什么?如何更改Junit在运行单元测试时获取数据和执行测试的行为?更具体一点,如果我要为一个需要两个参数的方法进行测试,如何使用我所提供的参数的所有排列组合对方法进行测试?如果我需要在茫茫的测试用例中只测试与特定类相关的用例该怎么做…….

          在这之前,先纠正一点------Junit4可以直接运行我们的某个方法,没有main入口函数是断然不行的。正如我之前给我们组的一个妹子讲Spring的时候告诉她,在测试方法中,对测试方法所在的类添加Spring的 (Compent注解或者为该类的成员变量添加)Resource注解并没有什么卵用,即Spring根本不会来扫描这个测试类,更不会为这个类注入属性值。为什么这么说呢?因为Spring是在测试类中由被@Before标注的方法所启动的,这时候,JVM已经将此测试类实例化了,而这并不是由Spring实例化的,Spring晚了一步,所以在Spring的容器中并没有此类的实例。那么Junit4真的有main方法吗?没错,既然它能直接运行我们的方法,那它必然自己为JVM提供了程序入口。其实在org.junit.runner包下,有个JUnitCore.class,其中就有一个 标准的main方法,这就是JUnit入口函数。如此看来,它其实和我们直接在自己的main方法中跑我们要测试的方法在本质上是一样的。

         接下来,我要说的就是Junit测试框架的新大陆,或者说是其核心组件,也正是讲师所没有讲到但却十分有用的东西------Runner,即Junit的运行器!

         Runner只是一个抽象类,表示用于运行Junit测试用例的工具,通过它可以运行测试并通知Notifier运行的结果。通常我们可以在待测方法所在的类之上使用@RunWith注解来为这个测试类指定一个特定的Runner。不过在很多情况下,我们并没有这么做,那是因为,我们使用了Junit的默认Runnner------BlockJunit4ClassRunner。当我们不为测试类添加@RunWith注解的时候,其实使用的就是这个Runner,它作为默认Runner只为我们提供了基本的基于Junit生命周期的测试注解。而有更多非常规的测试需求,则需要我们为测试类添加@RunWith注解并指定特定的Runner来完成!下面列出一些比较有用的Runner。

    一、Suit------它可以一次生执行全面在多个类中的测试用例,例如:

    @RunWith(Suite.class)
    @SuiteClasses({Person.class, People.class})
    public class TestSuitMain{
      //虽然这个类是空的,但依然可以运行Junit测试,运行时,它会将Person.class和//People.class中的所有测试用命都执行一遍!
    }

    二、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"}});
        }
    }

    三、Category------继承自Suit,更强大,它可以让我们对测试类中被测试的方法进行分类执行,例如Person对象具有一些属性,这些属性拥有get/set方法,同时还有一些普通方法。我们可以将获取属性的get方法和普通方法进行分类测试。如:

    public class PersonTest{
        @Category(AttributeFun.class)
        @Test
        public void testGetAge(){
            int age = person.getAge();
            assertEquals(3, age);
        }
        @Category(AttributeFun.class)
        @Test
        public void testGetName(){
            String name = person.getName();
            assertEquals("Willard", name);
        }
        @Category(BehaviorFun.class)
        @Test
        public void testTalk(){
            String message = person.talkTo("Jimy");
            assertNotNull(message);
        }
        @Category(BehaviorFun.class)
        @Test(timeout=200)
        public void testWalk(){
            person.walk();
        }
    }
    
    
    //对应的测试执行代码如下:
    @RunWith(Categories.class)
    @SuiteClasses(PersonTest.class)
    @IncludeCategory(AttributeFun.class)
    public class CategoryTest{
     //注意,如果不加@IncludeCategory注解,那么就和使用Suit具有一样的效果了。
    }

    四、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四个参数以类型合法的排列组合传给待没方法。因此输出的结果必然也有2x2=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};

    上展示了四个Junit运行器的使用示例,有这个四个运行器的支持,基本上大部分的测试需求得可以得到解决。当然Junit提供的功能远不止这些。除此之外,我们还可以使用Junit4提供的Rule/Assume/Assert等。

    其中使用Rule可以为单元测试指定测试规则,下面展示了这些可用的Rule:

           Verifier: 验证测试执行结果的正确性。

           ErrorCollector: 收集测试方法中出现的错误信息,测试不会中断,如果有错误发生测试结束后会标记失败。

           ExpectedException: 提供灵活的异常验证功能。

          Timeout: 用于测试超时的Rule。

          ExternalResource: 外部资源管理。

         TemporaryFolder: 在JUnit的测试执行前后,创建和删除新的临时目录。

         TestWatcher: 监视测试方法生命周期的各个阶段。

         TestName: 在测试方法执行过程中提供获取测试名字的能力。

    此外,Assume表示假设,但它实际是对待没方法的参数进行合法性校验的,如果校验不合格则直接抛异常,而不执行测试。这和Guava中的Predictions类似。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:判断对象是否为空。

          AssertSame:判断两个对象是否为同一个,不同于equals这里是使用“==”判断。

          AssertArrayEquals:判断两个数组是否相等。

    转载于:https://www.cnblogs.com/yepei/p/5649352.html

    展开全文
  • 前面几篇介绍完了JUnit的基本功能和基本使用场景以及用法,一般来说,现在的开发都是一个人一个模块,如果一个开发,写一个业务代码的类,然后利用前面的知识是可以完成最基础的单元测试,起码证明他写的方法是可以...

    前面几篇介绍完了JUnit的基本功能和基本使用场景以及用法,一般来说,现在的开发都是一个人一个模块,如果一个开发,写一个业务代码的类,然后利用前面的知识是可以完成最基础的单元测试,起码证明他写的方法是可以通过单元测试,至少开发会做正向成功的用例。这篇开始来学习JUnit作为一个单元测试框架的高级部分,我们先从TestSuite开始。

     

    1.测试套件 TestSuite

    不要笑,我们先来学习下Suite这个单词的发音,很多人会自然地念成(shoot,例如休特(哈哈)),其实这个单词念swēt(Swei,类似这个发音)。

    第一个特点,需要在这个class name之前一行添加上面两个注解

    第二个特点,也是核心,TestSuite就是用来管理多个Junit Test Case。

     

    2.举例练习

    为了模拟多个测试类,我们在新建一个测试类,只写一个测试方法。

    代码如下

    package test;
    
    import static org.junit.Assert.assertEquals;
    
    import org.junit.Test;
    
    public class HelloJunitTest {
    	@Test
    	public void test() {
    		assertEquals(5, "Hello".length());
    	}
    }
    

    开始我们的TestSuite创建过程。右键包,选择new-other..-Junit Test Suite

    自动帮我们导入上面提到两个Suite注解

    package test;
    
    import org.junit.runner.RunWith;
    import org.junit.runners.Suite;
    import org.junit.runners.Suite.SuiteClasses;
    
    @RunWith(Suite.class)
    @SuiteClasses({ HelloJunitTest.class, TrackingServiceTests.class })
    public class ProteinTrackerSuite {
    
    }
    

    可以在这个代码中点击class名称,右键-run as Jnuit,或者在工程中右键这个xxxSuite.java文件,然后run as JUnit。

    从这个图可以看到,两个被管理的TestCase中的JUnit用例都被执行了。试想一下,如果我管理好几十个这样class,每一个class都写十来个单元测试用例,那么这就是几百个单元测试用例,通过运行一个文件去执行几百个单元测试用例,也就是一条命令就可以实现,也就是可以放jenkins上做持续集成测试。如果TestSuite管理class很多,我们推荐这样格式去写suite文件。

    package test;
    
    import org.junit.runner.RunWith;
    import org.junit.runners.Suite;
    import org.junit.runners.Suite.SuiteClasses;
    
    @RunWith(Suite.class)
    @SuiteClasses({ 
    	HelloJunitTest.class, 
    	TrackingServiceTests.class 
    	// 接着写其他被测单元测试类
    })
    
    public class ProteinTrackerSuite {
    
    }
    

    TestSuite就介绍到这里。

     

     

     

     

     

    展开全文
  • Junit4.x高级用法详解(一)

    千次阅读 2014-06-19 13:47:39
    Junit4.8+的高级用法,包含了常用了Junit的注解使用方法和例子的详解。 包含了:Junit的超时测试、参数测试、分类测试、Mock测试、异常测试、suite测试


    最近整理代码的时候,总习惯把一些常用的工具类和方法等都写在junit中,这样可以方便于在想用的时候直接copy,在用junit的时候学到了一些比较有用的东西,记录如下:


    1.使用junit进行超时测试


        @Test(timeout=2000)
        public void testTimeout() throws InterruptedException {
            Thread.sleep(2000);
        }

        @Test(timeout=2000)
        public void testTimeout() throws InterruptedException {
            Thread.sleep(2001);
        }


    2.使用junit进行异常测试

    @Test(expected=IOException.class)
        public void testExceptions() throws InterruptedException {

            throw new RuntimeException();
        }
        
        
        @Test(expected=RuntimeException.class)
        public void testExceptions2() throws InterruptedException {
            
            throw new RuntimeException();
        }


    3.使用junit进行参数测试

    private SimpleDateFormat sdf;
        private String date;
        private String dateformat;
        private String expectedDate;
        
        
        
        public TestJunitParameter(String date, String dateformat,
                String expectedDate) {
            this.date = date;
            this.dateformat = dateformat;
            this.expectedDate = expectedDate;
        }
        
        @Parameters
        public static Collection getParamters() {

            String[][] object = {
                    { "2011-07-01 00:20:20", "yyyyMMdd", "20110701" },
                    { "2011-07-01 00:20:20", "yyyy年MM月dd日", "2011年07月01日" },
                    { "2011-07-01 00:20:20", "HH时mm分ss秒", "00时20分20秒" } };
            List<String[]> list = Arrays.asList(object);
            return  list;
        }

        @Test
        public void testJunitParameter() throws ParseException {
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date d = df.parse(this.date);
            sdf = new SimpleDateFormat(this.dateformat);
            String result = sdf.format(d);
            Assert.assertEquals(this.expectedDate, result);

        }


    4.使用junit进行Suite测试,不仅仅TestNg可以有suite哦~~~

    @RunWith(Suite.class)
    @SuiteClasses({TestDateFormat.class,TestIORead.class})
    public class TestSuite {
        
        
    }

    5.使用junit进行mock测试

    mock测试其实采用的Mockito进行,所以这里不记录了,将会有一个页单独的介绍Mockito.


    6.使用@Category进行分类测试

    public interface FastTests { /* category marker */ }
    public interface SlowTests { /* category marker */ }
    
    public class A {
      @Test
      public void a() {
        fail();
      }
    
      @Category(SlowTests.class)
      @Test
      public void b() {
      }
    }
    
    @Category({SlowTests.class, FastTests.class})
    public class B {
      @Test
      public void c() {
    
      }
    }
    
    @RunWith(Categories.class)
    @IncludeCategory(SlowTests.class)
    @SuiteClasses( { A.class, B.class }) // Note that Categories is a kind of Suite
    public class SlowTestSuite {
     // Will run A.b and B.c, but not A.a
    }
    
    @RunWith(Categories.class)
    @IncludeCategory(SlowTests.class)
    @ExcludeCategory(FastTests.class)
    @SuiteClasses( { A.class, B.class }) // Note that Categories is a kind of Suite
    public class SlowTestSuite {
      // Will run A.b, but not A.a or B.c
    }
    

    
    

    注:虽然Class B 也包含了SlowTests.class,但是其同时也包含了FastTests.class,因为我们在测试类的注解上加了@ExcludeCategory(FastTests.class),所以Class B的c方法是不会执行的


    成就与否,15%在于个人的才干和技能,而85%在于做人的技术和技巧。和大众融洽地相处,以和谐取悦于人,留意尊重别人的立场,让每个人都觉得自己是重要的,也就得到了讨人喜欢的秘决了。


    展开全文
  • JUnit4高级篇-由浅入深

    万次阅读 多人点赞 2015-03-14 16:00:31
    JUnit4是JUnit框架有史以来的最大改进,其主要目标便是利用Java5的Annotation特性简化测试用例的编写。 先简单解释一下什么是Annotation,这个单词一般是翻译成元数据。元数据是什么?元数据就是描述数据的数据。也...
    JUnit4是JUnit框架有史以来的最大改进,其主要目标便是利用Java5的Annotation特性简化测试用例的编写。

    先简单解释一下什么是Annotation,这个单词一般是翻译成元数据。元数据是什么?元数据就是描述数据的数据。也就是说,这个东西在Java里面可以用来和public、static等关键字一样来修饰类名、方法名、变量名。修饰的作用描述这个数据是做什么用的,差不多和public描述这个数据是公有的一样。想具体了解可以看Core    Java2。废话不多说了,直接进入正题。

    我们先看一下在JUnit 3中我们是怎样写一个单元测试的。比如下面一个类:
    public class AddOperation {
          public int add(int x,int y){
              return x+y;
          }
    }

    我们要测试add这个方法,我们写单元测试得这么写:
    import junit.framework.TestCase;
    import static org.junit.Assert.*;
    public class AddOperationTest extends TestCase{

          public void setUp() throws Exception {
          }

          public void tearDown() throws Exception {
          }

          public void testAdd() {
              System.out.println(\"add\");
              int x = 0;
              int y = 0;
              AddOperation instance = new AddOperation();
              int expResult = 0;
              int result = instance.add(x, y);
              assertEquals(expResult, result);
          }
    }

    可以看到上面的类使用了JDK5中的静态导入,这个相对来说就很简单,只要在import关键字后面加上static关键字,就可以把后面的类的static的变量和方法导入到这个类中,调用的时候和调用自己的方法没有任何区别。


    我们可以看到上面那个单元测试有一些比较霸道的地方,表现在:
    1.单元测试类必须继承自TestCase。
    2.要测试的方法必须以test开头。

    如果上面那个单元测试在JUnit 4中写就不会这么复杂。代码如下:
    import junit.framework.TestCase;
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    import static org.junit.Assert.*;

    /**
    *
    * @author bean
    */

    public class AddOperationTest extends TestCase{
        
          public AddOperationTest() {
          }

          @Before
          public void setUp() throws Exception {
          }

          @After
          public void tearDown() throws Exception {
          }

          @Test
          public void add() {
              System.out.println(\"add\");
              int x = 0;
              int y = 0;
              AddOperation instance = new AddOperation();
              int expResult = 0;
              int result = instance.add(x, y);
              assertEquals(expResult, result);
          }
        
    }
    我们可以看到,采用Annotation的JUnit已经不会霸道的要求你必须继承自TestCase了,而且测试方法也不必以test开头了,只要以@Test元数据来描述即可。
    从上面的例子可以看到在JUnit 4中还引入了一些其他的元数据,下面一一介绍:
    @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(“该方法还没有实现”),在执行的时候,仅会报告该方法没有实现,而不会运行测试方法。


     


    在Eclipse中使用JUnit4进行单元测试(初级篇)

     

    我们在编写大型程序的时候,需要写成千上万个方法或函数,这些函数的功能可能很强大,但我们在程序中只用到该函数的一小部分功能,并且经过调试可以确定,这一小部分功能是正确的。但是,我们同时应该确保每一个函数都完全正确,因为如果我们今后如果对程序进行扩展,用到了某个函数的其他功能,而这个功能有bug的话,那绝对是一件非常郁闷的事情。所以说,每编写完一个函数之后,都应该对这个函数的方方面面进行测试,这样的测试我们称之为单元测试。传统的编程方式,进行单元测试是一件很麻烦的事情,你要重新写另外一个程序,在该程序中调用你需要测试的方法,并且仔细观察运行结果,看看是否有错。正因为如此麻烦,所以程序员们编写单元测试的热情不是很高。于是有一个牛人推出了单元测试包,大大简化了进行单元测试所要做的工作,这就是JUnit4。本文简要介绍一下在Eclipse3.2中使用JUnit4进行单元测试的方法。

     

    首先,我们来一个傻瓜式速成教程,不要问为什么,Follow Me,先来体验一下单元测试的快感!

     

    首先新建一个项目叫JUnit_Test,我们编写一个Calculator类,这是一个能够简单实现加减乘除、平方、开方的计算器类,然后对这些功能进行单元测试。这个类并不是很完美,我们故意保留了一些Bug用于演示,这些Bug在注释中都有说明。该类代码如下:

     

    package andycpp;

    public class Calculator ...{
        private static int result;// 静态变量,用于存储运行结果
        public void add(int n) ...{
            result = result + n;
        }
        public void substract(int n) ...{
            result = result - 1;  //Bug: 正确的应该是 result =result-n
        }
        public void multiply(int n) ...{
        }         // 此方法尚未写好
        public void divide(int n) ...{
            result = result / n;
        }
        public void square(int n) ...{
            result = n * n;
        }
        public void squareRoot(int n) ...{
            for (; ;) ;            //Bug : 死循环
        }
        public void clear() ...{     // 将结果清零
            result = 0;
        }
        public int getResult() ...{
            return result;
        }
    }

     

    第二步,将JUnit4单元测试包引入这个项目:在该项目上点右键,点“属性”,如图:

     

     

     

     

     

    在弹出的属性窗口中,首先在左边选择“Java Build Path”,然后到右上选择“Libraries”标签,之后在最右边点击“Add Library…”按钮,如下图所示:

     

     

    然后在新弹出的对话框中选择JUnit4并点击确定,如上图所示,JUnit4软件包就被包含进我们这个项目了。

     

        第三步,生成JUnit测试框架:在Eclipse的Package Explorer中用右键点击该类弹出菜单,选择“New à JUnit Test Case”。如下图所示:

     

     

     

    在弹出的对话框中,进行相应的选择,如下图所示:

     

     

        点击“下一步”后,系统会自动列出你这个类中包含的方法,选择你要进行测试的方法。此例中,我们仅对“加、减、乘、除”四个方法进行测试。如下图所示:

     

     

     

    之后系统会自动生成一个新类CalculatorTest,里面包含一些空的测试用例。你只需要将这些测试用例稍作修改即可使用。完整的CalculatorTest代码如下:

     

    package andycpp;

    import static org.junit.Assert.*;
    import org.junit.Before;
    import org.junit.Ignore;
    import org.junit.Test;

    public class CalculatorTest ...{

        private static Calculator calculator = new Calculator();
       
        @Before
        public void setUp() throws Exception ...{
            calculator.clear();
        }

        @Test
        public void testAdd() ...{
            calculator.add(2);
            calculator.add(3);
            assertEquals(5, calculator.getResult());
        }

        @Test
        public void testSubstract() ...{
            calculator.add(10);
            calculator.substract(2);
            assertEquals(8, calculator.getResult());
        }

        @Ignore("Multiply() Not yet implemented")
        @Test
        public void testMultiply() ...{
        }

        @Test
        public void testDivide() ...{
            calculator.add(8);
            calculator.divide(2);
            assertEquals(4, calculator.getResult());
        }
    }

    第四步,运行测试代码:按照上述代码修改完毕后,我们在CalculatorTest类上点右键,选择“Run As à JUnit Test”来运行我们的测试,如下图所示:

     

     

     

    运行结果如下:

     

     

     

     

     

    进度条是红颜色表示发现错误,具体的测试结果在进度条上面有表示“共进行了4个测试,其中1个测试被忽略,一个测试失败”

     

                至此,我们已经完整体验了在Eclipse中使用JUnit的方法。在接下来的文章中,我会详细解释测试代码中的每一个细节!


    在Eclipse中使用JUnit4进行单元测试(中级篇)

     

    我们继续对初级篇中的例子进行分析。初级篇中我们使用Eclipse自动生成了一个测试框架,在这篇文章中,我们来仔细分析一下这个测试框架中的每一个细节,知其然更要知其所以然,才能更加熟练地应用JUnit4。

     

    一、包含必要地Package

     

    在测试类中用到了JUnit4框架,自然要把相应地Package包含进来。最主要地一个Package就是org.junit.*。把它包含进来之后,绝大部分功能就有了。还有一句话也非常地重要“import static org.junit.Assert.*;”,我们在测试的时候使用的一系列assertEquals方法就来自这个包。大家注意一下,这是一个静态包含(static),是JDK5中新增添的一个功能。也就是说,assertEquals是Assert类中的一系列的静态方法,一般的使用方式是Assert. assertEquals(),但是使用了静态包含后,前面的类名就可以省略了,使用起来更加的方便。

     

    二、测试类的声明

     

    大家注意到,我们的测试类是一个独立的类,没有任何父类。测试类的名字也可以任意命名,没有任何局限性。所以我们不能通过类的声明来判断它是不是一个测试类,它与普通类的区别在于它内部的方法的声明,我们接着会讲到。

     

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

     

    你要测试哪个类,那么你首先就要创建一个该类的对象。正如上一篇文章中的代码:

     

    private static Calculator calculator = new Calculator();

     

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

     

    四、测试方法的声明

     

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

     

    五、编写一个简单的测试方法。

     

    首先,你要在方法的前面使用@Test标注,以表明这是一个测试方法。对于方法的声明也有如下要求:名字可以随便取,没有任何限制,但是返回值必须为void,而且不能有任何参数。如果违反这些规定,会在运行时抛出一个异常。至于方法内该写些什么,那就要看你需要测试些什么了。比如:

     

     

       

    @Test

        public void testAdd() ...{

              calculator.add(2);

              calculator.add(3);

              assertEquals(5, calculator.getResult());

        }

     

    我们想测试一下“加法”功能时候正确,就在测试方法中调用几次add函数,初始值为0,先加2,再加3,我们期待的结果应该是5。如果最终实际结果也是5,则说明add方法是正确的,反之说明它是错的。assertEquals(5, calculator.getResult());就是来判断期待结果和实际结果是否相等,第一个参数填写期待结果,第二个参数填写实际结果,也就是通过计算得到的结果。这样写好之后,JUnit会自动进行测试并把测试结果反馈给用户。

     

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

     

    如果你在写程序前做了很好的规划,那么哪些方法是什么功能都应该实现定下来。因此,即使该方法尚未完成,他的具体功能也是确定的,这也就意味着你可以为他编写测试用例。但是,如果你已经把该方法的测试用例写完,但该方法尚未完成,那么测试的时候一定是“失败”。这种失败和真正的失败是有区别的,因此JUnit提供了一种方法来区别他们,那就是在这种测试函数的前面加上@Ignore标注,这个标注的含义就是“某些方法尚未完成,暂不参与此次测试”。这样的话测试结果就会提示你有几个测试被忽略,而不是失败。一旦你完成了相应函数,只需要把@Ignore标注删去,就可以进行正常的测试。

     

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

     

    Fixture的含义就是“在某些阶段必然被调用的代码”。比如我们上面的测试,由于只声明了一个Calculator对象,他的初始值是0,但是测试完加法操作后,他的值就不是0了;接下来测试减法操作,就必然要考虑上次加法操作的结果。这绝对是一个很糟糕的设计!我们非常希望每一个测试都是独立的,相互之间没有任何耦合度。因此,我们就很有必要在执行每一个测试之前,对Calculator对象进行一个“复原”操作,以消除其他测试造成的影响。因此,“在任何一个测试执行之前必须执行的代码”就是一个Fixture,我们用@Before来标注它,如前面例子所示:

     

     

          @Before

          public void setUp() throws Exception ...{

               calculator.clear();

          }

     

    这里不在需要@Test标注,因为这不是一个test,而是一个Fixture。同理,如果“在任何测试执行之后需要进行的收尾工作”也是一个Fixture,使用@After来标注。由于本例比较简单,没有用到此功能。

     


    在Eclipse中使用JUnit4进行单元测试(高级篇)

     

    一、高级Fixture

     

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

     

    二、限时测试。

     

    还记得我在初级篇中给出的例子吗,那个求平方根的函数有Bug,是个死循环:

     

     

       

    public void squareRoot(int n) ...{

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

     }

     

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

     

    @Test(timeout = 1000)
    public void squareRoot() ...{
            calculator.squareRoot(4);
            assertEquals(2, calculator.getResult());
    }

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

     

    三、 测试异常

    JAVA中的异常处理也是一个重点,因此你经常会编写一些需要抛出异常的函数。那么,如果你觉得一个函数应该抛出异常,但是它没抛出,这算不算Bug呢?这当然是Bug,并JUnit也考虑到了这一点,来帮助我们找到这种Bug。例如,我们写的计算器类有除法功能,如果除数是一个0,那么必然要抛出“除0异常”。因此,我们很有必要对这些进行测试。代码如下:

      @Test(expected = ArithmeticException.class)
      public void divideByZero() ...{
            calculator.divide(0);
      }

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

     

    四、     Runner (运行器)

    大家有没有想过这个问题,当你把测试代码提交给JUnit框架后,框架如何来运行你的代码呢?答案就是——Runner。在JUnit中有很多个Runner,他们负责调用你的测试代码,每一个Runner都有各自的特殊功能,你要根据需要选择不同的Runner来运行你的测试代码。可能你会觉得奇怪,前面我们写了那么多测试,并没有明确指定一个Runner啊?这是因为JUnit中有一个默认Runner,如果你没有指定,那么系统自动使用默认Runner来运行你的代码。换句话说,下面两段代码含义是完全一样的:

    import org.junit.internal.runners.TestClassRunner;
    import org.junit.runner.RunWith;

    //使用了系统默认的TestClassRunner,与下面代码完全一样

    public class CalculatorTest ...{...} 

    @RunWith(TestClassRunner.class)
    public class CalculatorTest ...{...}

    从上述例子可以看出,要想指定一个Runner,需要使用@RunWith标注,并且把你所指定的Runner作为参数传递给它。另外一个要注意的是,@RunWith是用来修饰类的,而不是用来修饰函数的。只要对一个类指定了Runner,那么这个类中的所有函数都被这个Runner来调用。最后,不要忘了包含相应的Package哦,上面的例子对这一点写的很清楚了。接下来,我会向你们展示其他Runner的特有功能。

     

    五、 参数化测试。

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

     

    import org.junit.AfterClass;
    import org.junit.Before;
    import org.junit.BeforeClass;
    import org.junit.Test;
    import static org.junit.Assert.*;

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

     

    import static org.junit.Assert.assertEquals;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.junit.runners.Parameterized;
    import org.junit.runners.Parameterized.Parameters;
    import java.util.Arrays;
    import java.util.Collection;

    @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());
        }
     }

    下面我们对上述代码进行分析。首先,你要为这种测试专门生成一个新的类,而不能与其他测试共用同一个类,此例中我们定义了一个SquareTest类。然后,你要为这个类指定一个Runner,而不能使用默认的Runner了,因为特殊的功能要用特殊的Runner嘛。@RunWith(Parameterized.class)这条语句就是为这个类指定了一个ParameterizedRunner。第二步,定义一个待测试的类,并且定义两个变量,一个用于存放参数,一个用于存放期待的结果。接下来,定义测试数据的集合,也就是上述的data()方法,该方法可以任意命名,但是必须使用@Parameters标注进行修饰。这个方法的框架就不予解释了,大家只需要注意其中的数据,是一个二维数组,数据两两一组,每组中的这两个数据,一个是参数,一个是你预期的结果。比如我们的第一组{2, 4},2就是参数,4就是预期的结果。这两个数据的顺序无所谓,谁前谁后都可以。之后是构造函数,其功能就是对先前定义的两个参数进行初始化。在这里你可要注意一下参数的顺序了,要和上面的数据集合的顺序保持一致。如果前面的顺序是{参数,期待的结果},那么你构造函数的顺序也要是“构造函数(参数, 期待的结果)”,反之亦然。最后就是写一个简单的测试例了,和前面介绍过的写法完全一样,在此就不多说。

     

    六、 打包测试。

    通过前面的介绍我们可以感觉到,在一个项目中,只写一个测试类是不可能的,我们会写出很多很多个测试类。可是这些测试类必须一个一个的执行,也是比较麻烦的事情。鉴于此,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,来表明这个类是一个打包测试类。我们把需要打包的类作为参数传递给该标注就可以了。有了这两个标注之后,就已经完整的表达了所有的含义,因此下面的类已经无关紧要,随便起一个类名,内容全部为空既可。

    展开全文
  • JUnit高级用法之@RunWith

    千次阅读 2017-10-14 22:02:00
    为什么80%的码农都做不了架构师?>>> ...
  • JUnit Jupiter是JUnit5扩展的新的编程模型和扩展模型,用来编写测试用例。Jupiter子项目为在平台上运行Jupiter的测试提供了一个TestEngine (测试引擎)。 JUnit Vintage提供了一个在平台上运行JUnit 3和JUnit 4的...
  • junit5 入门系列教程-01-junit5 简单入门例子

    万次阅读 多人点赞 2018-06-24 16:36:15
    Junit5 简介 JDK 支持 快速开始 Maven 导入 测试案例 变化 文档参考 Junit5 JUnit5 is the next generation of JUnit. 目标是为JVM上的开发人员端测试创建一个最新的基础。这包括关注Java 8和以上版本...
  • springboot使用junit5/junit4

    千次阅读 2020-01-31 17:06:31
    JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage 其中 JUnit平台,其主要作用是在JVM上启动测试框架。它定义了一个抽象的...JUnit Jupiter,包含了JUnit5最新的编程模型和扩展机制。 JUnit Vint...
  • Junit5系列-什么是Junit5?

    千次阅读 2019-01-21 17:57:46
    目录Junit5简介JDK 支持Maven 导入所有组件介绍JUnit PlatformJUnit JupiterJUnit VintageJunit5 BOM Junit5 官网:JUnit5 is the next generation of JUnit. 注意:以下内容绝大部分翻译自官网 目标是为JVM上的...
  • 单元测试之JUnit5入门

    万次阅读 2018-02-07 13:31:54
    关于JUnit5 与以前版本的JUnit不同,JUnit 5由三个不同子项目中的几个不同模块组成。 JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage JUnit Platform是基于JVM的运行测试的基础框架在,它定义了...
  • JUnit 5 简介

    千次阅读 2017-12-05 09:06:02
    转自:https://www.ibm.com/developerworks/cn/java/j-introducing-junit5-part1-jupiter-api/index.html  https://www.ibm.com/developerworks/cn/java/j-introducing-junit5-part2-vintage-jupiter-extension
  • JUnit 5JUnit 4比较

    万次阅读 多人点赞 2019-01-21 15:11:34
    JUnit 5旨在调整java 8样式的编码,并且比JUnit 4更强大和灵活。在这篇文章中,JUnit 5 vs JUnit 4,我们将关注junit 4和junit 5之间的一些主要差异。 1. JUnit 5 vs JUnit 4 - 注释 两个版本中的大多数注释都是...
  • Junit5 注解 @Test @ParameterizedTest @RepeatedTest @TestFactory @TestInstance @TestTemplate @DisplayName @BeforeEach @AfterEach @BeforeAll @AfterAll @Nested @Tag @Disabled @ExtendWith 元注释,注释 .....
  • 目录 目录 测试接口和默认方法 ...JUnit Jupiter允许在接口默认方法上声明@Test、@RepeatedTest、@ParameterizedTest、@TestFactory、@TestTemplate、@BeforeEach和@AfterEach。 如果测试接口或测试类被@Tes...
  • Junit5 jar包

    2018-06-13 18:26:49
    Junit5的jar包,可用来测试Lambda表达式等。
  • JUnit 5 快速上手(从 JUnit 4 到 JUnit 5)

    千次阅读 2017-09-18 15:59:21
    一直在关注 JUnit 5 的演进,自两年前首个 ALPHA 版后,经历了 6 的 Milestone, 3 个 RC 终于在 2017/09/10 正式发布了。其实还从未对其深究过,今天算是正式开始体验。 不像以往的版本,JUnit 5 现在是三个模块的...
  • Junit5系列-Junit5中Assertions断言类

    千次阅读 2019-01-08 20:28:53
    junit5中的JUnit Jupiter提供了Assertions类来替代了junit4中的Assert类并且添加了一些新的方法,所以工作过程中完全可以使用Assertions代替Assert类。 其包名称为:org.junit.jupiter.api.Assertions Assertions中...
  • junit5-r5.3.0

    2018-12-25 12:48:14
    junit5-r5.3.0
  • JUnit Jupiter附带了许多JUnit 4拥有的断言方法,并添加了一些可以很好地用于Java 8 lambdas的断言方法。 所有JUnit木星断言都是 org.junit.jupiter.api.Assertions 中的静态方法断言类。 实例 AssertTest....
  • JUnit5 + JMockit 知识整理

    千次阅读 2019-01-22 15:15:47
    JUnit5 https://junit.org/junit5/ 基本概念 JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage JUnit Platform: JUnit5 框架,复杂启动并加载运行 Test Cases; JUnit Jupiter: JUnit5 Test Case 开发...
  • JUnit Jupiter提供了通过使用@RepeatedTest注释方法并指定所需重复次数的能力。重复测试的每次调用都表现为对相同生命周期回调和扩展的完全支持的常规@Test方法的执行。 下面的示例演示如何声明一个名为...
  • 目录 目录 依赖注入 TestInfoParameterResolver RepetitionInfoParameterResolver TestReporterParameterResolver @ExtendWith ...在所有以前的JUnit版本中,都不允许测试构造函数或方法具有参数(至少不...
  • Junit5系列-Junit5中@Disabled禁止执行

    千次阅读 2019-01-09 17:13:34
    这个@Disabled注解代替了Junit4中的@Ignore注解,功能相同。 案例分析 其中每个方法的作用代码中的注释写的已经非常清楚了,就不再赘述了. 大家最好将代码自己测试一遍,可以加深理解与记忆! 案例代码: import org...
  • Java单元测试工具JUnit 5新特性一览

    万次阅读 多人点赞 2016-02-14 12:35:27
    Java单元测试工具JUnit 5新特性一览作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszsJUnit是最流行的开源Java单元测试工具,目前它的稳定版是4.12版。JUnit 4是...
  • Junit 4 与Junit 5区别

    2020-10-20 15:10:17
    所需JDK Junit 4 Junit 5 ...需要 Java 5 或以上版本 ...Junit 4 所有的东西在一...Junit 5JUnit Platform, JUnit Jupiter 和 JUnit Vintage 三个子项目组成: JUnit Platform 定义了 test engine API,用于开发在
  • spring boot Junit5单元测试

    千次阅读 2019-08-13 13:22:38
    依赖 <!--单元测试--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> ...
  • JUnit4 与 JUnit 5 常用注解对比

    万次阅读 多人点赞 2018-06-06 10:51:16
    JUnit4 与 JUnit 5 常用注解对比|JUnit4|JUnit5|说明||-|-|-||@Test|@Test|表示该方法是一个测试方法。JUnit5与JUnit 4的@Test注解不同的是,它没有声明任何属性,因为JUnit Jupiter中的测试是基于它们自己的专用...
  • junit 5测试异常处理 JUnit 5带来了令人敬畏的改进,并且与以前的版本有很大不同。... 为了演示JUnit 5用法,我使用了我的长期unit-testing-demo Github项目,因为该项目已经包含许多单元测试示例: https : //...
  • Spring5整合Junit4、Junit5测试bean

    千次阅读 2019-07-25 18:02:07
    Spring5整合Junit4(要求Junit4的版本不能低于4.12) //测试类 @RunWith(SpringJUnit4ClassRunner.class)//指定Junit4使用Spring提供的测试环境 @ContextConfiguration("classpath:applicationContext.xml")//指定...
  • 目录 目录 按条件执行测试用例 Composed Annotations ...JUnit Jupiter中的ExecutionCondition扩展API允许开发人员以编程方式启用或禁用容器或测试。 这种情况最简单的例子是内置的DisabledC...

空空如也

1 2 3 4 5 ... 20
收藏数 308,438
精华内容 123,375
关键字:

junit高级用法