0%

Hello , JUnit -- IDEA and Maven

知道PHP有一个phpUnit做单元测试,所以在写java的时候也顺便了解一下JUnit如何代码做单元测试

概述

在IDEA和Maven的下面,如何使用JUnit去编写测试单元。这里我使用的JUnit版本是3.8.1,JUnit4.0以上的版本在具体用法上会有些许差异。

栗子

项目源代码: jsp-basic

maven配置:

一般而言,使用maven构建的项目都会自带junit,并且/src/test目录就是专门供写测试单元的目录。

idea配置:
我使用的是Mac版本的idea,首先是安装JUnit的plugin,在preference->plugin->Browse repositories->输入JUnit,安装JUnit Generator V2.0。
它具体的样子应该长这样:
plugin.jpg

安装成功后,我们执行JUnit的test案例时,应该会出现以下结果:

test.jpg

可以看见,左边的是对应我测试哪个类,然后该类下测试的方法是什么等等,右边的输出表示了测试结果和输出信息。

junit3.8下的测试代码

Calculate类:

package com.liumapp.jspbasic.util;

/**
 * Created by liumapp on 6/26/17.
 * E-mail:liumapp.com@gmail.com
 * home-page:http://www.liumapp.com
 */
public class Calculate {

    public int add (int a , int b ) {
        return a + b;
    }

    public int subtract (int a , int b ) {
        return a - b;
    }

    public int multiply (int a , int b) {
        return a * b;
    }

    public int divide (int a , int b) {
        return a / b;
    }
}

CalculateTest类:

package com.liumapp.jspbasic.util;


import junit.framework.TestCase;

/**
 * Created by liumapp on 6/26/17.
 * E-mail:liumapp.com@gmail.com
 * home-page:http://www.liumapp.com
 */
public class CalculateTest
        extends TestCase
{
    /**
     * Create the test case
     *
     * @param testName name of the test case
     */
    public CalculateTest( String testName )
    {
        super( testName );
    }

    /**
     * Rigourous Test :-)
     */
    public void testAdd()
    {
        System.out.println("test add begin");
        assertEquals(6 , new Calculate().add(3 ,3));
    }

    public void testSubtract ()
    {
        System.out.println("test subtract begin");
        assertEquals(1 , new Calculate().subtract(2  , 1));
    }

    public void testDivide ()
    {
        System.out.println("test divide begin");
        assertEquals(1 , new Calculate().divide(1 ,1));
    }

    public void testMultiply ()
    {
        System.out.println("test multiply begin");
        assertEquals(1 , new Calculate().multiply(1,1));
    }

}

CalculateTest类的运行结果即为上面的截图所示。
根据上面的栗子可以很简单的看出来,JUnit最基本的测试类编写规则:

  • 类要继承junit.framework.TestCase
  • 测试方法命名的规则为:test+要测试的类的方法名
  • 利用asset断言去判断结果的正确与否,至于代码逻辑的正确与否,junit似乎爱莫能助。

上面的栗子是没有错误发生的,如果我们将testAdd()方法里面的assetEquals(6 , new Calculate().add(3,3));改为assetEquals(5, new Calculate().add(3 ,3));那么测试将会报错,因为3+3=6这与预期的5不符合。

JUnit Generator V2.0的使用

这一款插件跟idea自带的JUnit不同,区别在于它是用来自动生成测试单元的,具体用法:

比如我们上面提到的Calculate类,如果要自动生成它的测试单元,那步骤是这样的:

  • 进入这个类的视图
  • 按Ctrl+回车,在开打的下拉框中选择JUnit3
  • 代码自动生成成功,但是这款插件在设计的时候似乎并没有考虑用户有使用maven的情况,所以它生成的测试单元我们还需要稍作改动,使之跟我们的项目目录结构相对应。

自动生成的测试单元目录结构如图所示:

testResult.jpg

/src/main/java/test/下为自动生成的测试单元,而我们实际上应该使用的测试单元在/src/test/java/目录下,所以改动的时候只需要移动一下即可。或者有某位大神知道怎么改动这个插件让它变聪明点,请在评论区指导一下。

JUnit3和JUnit4的区别

这里列出来的区别并不完善,我发现了新的区别会在以后进行更新。

  • JUnit3 需要继承junit.framework.TestCase,JUnit4不需要继承任何父类
  • JUnit4新引入了注解机制,JUnit3没有。
  • JUnit4 对比JUnit3 新增了setupbeforeclass和teardownafterclass这两个方法

JUnit运行流程

假设有下面一个测试类:

package com.liumapp.jspbasic;

import com.liumapp.jspbasic.util.Calculate;
import junit.framework.TestCase;

/**
 * Created by liumapp on 6/26/17.
 * E-mail:liumapp.com@gmail.com
 * home-page:http://www.liumapp.com
 */
public class JunitFlowTest extends TestCase {


    @Override
    protected void setUp() throws Exception {
        System.out.println("this is setUp");
    }

    @Override
    protected void tearDown() throws Exception {
        System.out.println("this is tearDown");
    }

    /**
     * Rigourous Test :-)
     */
    public void testAdd()
    {
        System.out.println("test add begin");
        assertEquals(6 , new Calculate().add(3 ,3));
    }

    public void testSubtract ()
    {
        System.out.println("test subtract begin");
        assertEquals(1 , new Calculate().subtract(2  , 1));
    }
}

它在运行的时候,打印的结果为:

this is setUp
test add begin
this is tearDown
this is setUp
test subtract begin
this is tearDown

由此可见,JUnit在每一个测试方法执行之前,会去执行一遍setUp方法,之后完成之后,会去执行一遍tearDown方法,在JUnit4里面,新增了setUpBeforeClass和tearDownAfterClass两个方法,第一个表示JUnit测试类开始测试的时候执行一遍,第二个表示JUnit类测试结束的时候执行。(有点类似构造函数和析构函数)

setUpBeforeClass

它会在所有方法被调用前执行,而且该方法是静态的,所以当测试类被加载后接着就会运行它,而且在内存中它只会存在一份实例,比较适合用来加载配置文件。

tearDownAfterClass

通常用来清理资源,比如关闭数据库的连接等。

JUnit4的常用注解

  • @Test:将一个普通的方法修饰成为一个测试方法
    • @Test(expected=ArithmeticException.class): 抛出异常
    • @Test(timeout=毫秒):最大操作时间
  • @BeforeClass:它会在所有的方法运行前被执行,static修饰,相当于setUpBeforeClass
  • @AfterClass:它会在所有的方法运行结束后被执行,static修饰,相当于tearDownAfterClass
  • @Before:会在每一个测试方法被运行前执行一次
  • @After:会在每一个测试方法运行后被执行一次
  • @Ignore:所修饰的测试方法将会被忽略,不会被执行
  • @RunWith:可以更改测试运行器 , 比如:org.junit.runner.Runner

批量测试

在/src/test下创建一个TestAll类,其代码如下:

package com.liumapp.jspbasic;

import com.liumapp.jspbasic.util.CalculateTest;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

/**
 * Created by liumapp on 6/26/17.
 * E-mail:liumapp.com@gmail.com
 * home-page:http://www.liumapp.com
 */
public class TestAll extends TestCase{

    public static Test suite()
    {
        TestSuite ts = new TestSuite();

        ts.addTestSuite(CalculateTest.class);
        ts.addTestSuite(JunitFlowTest.class);

        return ts;
    }

}

对于JUnit3.8而言,我们要利用TestSuite的addTestSuite来进行批量测试,上面的代码就是把CalculateTest和JunitFlowTest这两个测试包含进去,所以我们在运行TestAll这个测试类的时候,CalculateTest和JunitFlowTest这两个测试将会被执行。

而对于JUnit4而言,则是通过注解机制来实现:

@RunWith(Suite.class)
@Suite.SuiteClass({CalculateTest.class,JunitFlowTest.class})
public class TestAll {
  //内容为空
}

参数化测试

参数化测试目前来看,在JUnit4下支持比较良好,JUnit3.8下我暂时没有找到一个合适的解决方案。

package com.liumapp.jspbasic;

import com.liumapp.jspbasic.util.Calculate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.util.Arrays;
import java.util.Collection;

import junit.framework.TestCase;

/**
 * Created by liumapp on 6/26/17.
 * E-mail:liumapp.com@gmail.com
 * home-page:http://www.liumapp.com
 */
@RunWith(Parameterized.class)
public class ParameterTest {
    int expected = 0;
    int input1 = 0;
    int input2 = 0;

    public ParameterTest( int expected , int input1 , int input2) {
        this.expected = expected;
        this.input1 = input1;
        this.input2 = input2;
    }

    @Parameterized.Parameters
    public static Collection<Object[]> t() {
        return Arrays.asList(new Object[][]{
                {3,1,2},
                {4,2,2}
        });
    }

    @Test
    public void testAdd()
    {
        System.out.println("test add begin: while expected is " + expected);
        TestCase.assertEquals(expected , new Calculate().add(input1 ,input2));
    }

}

上面的测试代码在执行后,将会以3,1,2和4,2,2这两组数据去测试Calculate().add()方法。