单元测试JUnit

  在众多的分类中,与开发人员关系最紧密的莫过于单元测试了。其他种类的测试基本上都是由专门的测试人员来完成,只有单元测试是完全由开发人员来完成的。那么今天我们就来说说什么是单元测试,为什么要进行单元测试,以及如更好的何进行单元测试。

单元测试目的是什么

   执行单元测试,是为了证明某段代码的行为确实和开发者所期望的一致。

  白盒测试和单元测试的区别:1、测试目的,一个是测试程序的整体逻辑,另一个是测试程序中一个独立的模块;2、通常的执行人员不一样,白盒一般是由专门的白盒测试人员完成,单元测试一般由程序员自己完成。

什么是JUnit

   JUnit是用于编写可复用测试集的简单框架,是xUnit的一个子集。xUnit是一套基于测试驱动开发的测试框架,有PythonUnit、CppUnit、JUnit等。
  JUnit是一个开源的Java单元测试框架,是 Java的标准单元测试库,是非常重要第三方 Java 库,由 Kent Beck 和 Erich Gamma 开发。
  Junit测试是程序员主导的测试,即所谓白盒测试,因为程序员知道被测试的软件如何(How)完成功能和完成什么样(What)的功能。
  多数Java的开发环境都已经集成了JUnit作为单元测试的工具,比如Eclipse。

为什么要使用测试工具

  测试框架可以帮助我们对编写的程序进行有目的地测试,帮助我们最大限度地避免代码中的bug,以保证系统的正确性和稳定性。
  很多人对自己写的代码,测试时就简单写main,然后system.out.println()输出到控制台观察结果。这样非常枯燥繁琐,不规范。缺点:测试方法不能一起运行,测试结果要程序猿自己观察才可以判断程序逻辑是否正确。
  JUnit的断言机制,可以直接将我们的预期结果和程序运行的结果进行一个比对,确保对结果的可预知性。

JUnit使用步骤

  1、使用Eclipse创建一个项目,导入JUnit相关包,因为eclipse集成了JUnit框架,所以可以直接右键项目,选择Build Path → 点击Add Libraries → 选择JUnit → 点击Next,选择JUnit版本,然后Finish


  本人使用的版本是JUnit4.12.0

  2、先写要测试的业务逻辑类,假设要测验的是编写好的一个工具类Calculate.java,其源代码如下:

public final class Calculate {
    private Calculate(){
        throw new Error("请不要实例化我!");
    }
    public static int add(int a,int b){
        return a + b;
    }

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

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

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

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

  3、在packageExplorer视图中,右击Calculate.java.java源文件,选择 new→ JUnit Test Case。尽量让测试文件与业务文件处在不同的包结构下,这样便于管理。

  4、弹出的对话框,暂时不做修改,直接点击next(下图中创建的存根setUpBeforeClass()等四个函数都选中,它们的作用后续再解释)。

  5、勾选Calculate.java类中要测试的方法,点击Finish,这里测试Calculate工具类的所有方法。

  6、修改生成的测试类代码。上面步骤生成的测试类只包含测试方法的模板,并没有具体的测试细节,修改成如下:(其中之前选中的四个存根方法和Calculate类要测试的方法对应的测试函数都已经自动创建好,只要写函数体即可)

public class JUnitFlowTest {

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
         System.out.println("this is setUpBeforeClass()...");  
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
         System.out.println("this is tearDownAfterClass()...");  
    }

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

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

    @Test
    public void testAdd() {
        System.out.println("this is testAdd()...");  
        int result = Calculate.add(2, 3);
        assertEquals("加法有问题", 5, result);  
    }

    @Test
    public void testSubstract() {
        System.out.println("this is testSubstract()...");  
        int result = Calculate.substract(12, 2);
        assertEquals("减法有问题", 10, result); 
    }

    @Test
    public void testMultiply() {
        int result = Calculate.multiply(2, 3);  
        assertEquals("乘法有问题", 6, result);  
    }

    @Test
    public void testDivide() {
        int result = Calculate.divide(6,2);  
        assertEquals("除法有问题", 3, result);  
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

  存根方法的标注说明:
  ①@BeforeClass修饰的方法会在所有方法被调用前被执行,而且该方法是静态的,所以当测试类被加载后接着就会运行它,而且在内存中它只会存在一份实例,它比较适合加载配置文件,进行初始化等等。
  ②@AfterClass所修饰的方法会在所有方法被调用后被执行,通常用来对资源的清理,如关闭数据库的连接。
  ③@Before和@After会在每个测试方法的前后各执行一次。

  所以,一个JUnit单元测试用例执行顺序为: @BeforeClass -> @Before -> @Test -> @After -> @AfterClass;
  每一个测试方法的调用顺序为: @Before -> @Test -> @After;
  所有加注解的方法必须为public。

   7、选中JUnitFlowTest.java这个文件,右键选择运行方式为JUnit测试。即可查看单元测试的结果。

   右侧JUnit测试的输出结果表明在0.026秒内完成了对Calculate类指定方法的测试,错误和故障次数均为0(只要是绿色就表示一切正常,红色代表有错误或者故障发生)。从控制台的输出可以看出setUpBeforeClass()方法和tearDownAfterClass()方法只在测试的开始和结束处分别运行一次,而setUp()方法和tearDown()方法则在每一个方法的开始和结束之后分别运行一次。

测试失败的情况

  1、上面的测试中没有出现任何问题,一次通过,下面将减法的测试故意设置成有问题的,修改代码如下,再看测试结果。

@Test
    public void testSubstract() {
        System.out.println("this is testSubstract()...");  
        int result = Calculate.substract(12, 2);
        assertEquals("减法有问题", 1000, result); 
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6


   总共有4个测试方法,运行了4个方法;其中故障次数(Failures)为1。这里减法计算出12 - 2 = 10而不是1000,即断言机制判断预期结果与实际结果不一致,引发了1 次故障,这次故障导致了测试失败。

  2、上面测试中因为一个测试方法的实际结果与预期结果不一致导致了故障(Failures)而不是导致错误(Errors),那么错误与故障有什么区别,怎样引发错误呢?将测试代码中的减法和除法测试函数修改成如下形式后,重新运行测试。

@Test
    public void testSubstract() {
        System.out.println("this is testSubstract()...");  
        int result = Calculate.substract(12, 2);
        assertEquals("减法有问题", 1000, result); //实际值是预期值不一样
    }

@Test
    public void testDivide() {
        int result = Calculate.divide(6,0);  //除数为0,无法计算
        assertEquals("除法有问题", 3, result);  
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

  再次运行测试后,测试依然失败。但是,testSubstract()方法是故障(failure)。


  testDivide()方法是错误(error)

  故障(Failure)一般由单元测试使用的断言方法判断失败所引起的,这经表示测试点发现了问题,就是说程序输出的结果和我们预期的不一样。
  错误(Error)是由代码异常引起的,它可以产生于测试代码本身的错误,也可以是被测试代码中的一个隐藏的bug

JUnit跟用main方法测试有什么区别

  首先,JUnit的结果更加直观,直接根据状态条的颜色即可判断测试是否通过,而用main方法你需要去检查它的输出结果,然后跟自己的期望结果进行对比,才能知道是否测试通过。有一句话能够很直观的说明这一点——keeps the bar green to keeps the code clean。意思就是说,只要状态条是绿色的,那么你的代码就是正确的

  第二点,JUnit让我们同时运行多个测试变得非常方便:首先我们要再多建几个JUnit测试类,把它们统一放在一个包下,与业务逻辑类分开。然后在我们多个测试类所在包上右击选择Run As—Run Configurations,如下图:


  选择第二项“运行所选项目、包或者源文件夹中的所有测试”,然后点击“运行”效果如下:


  可以看到,我们本次测试运行了同一个包下的三个测试类,共11个方法,这种方便的效果在测试实例越多的情况下,体现的越明显。至于main方法运行多个测试,想想就觉得非常麻烦。


不要用JUnit进行多线程测试

  Junit本身是不支持普通的多线程测试的,这是因为Junit的底层实现上,是用System.exit()退出用例执行的。JVM都终止了,在测试线程启动的其他线程自然也无法执行。Junit只管自己的运行,当Junit执行完毕后,就会关闭程序,不会关心是否还有自己启动的后台线程在运行。当Junit运行完毕后,如果后台线程还没有执行完毕,那么也就不会再执行了。测试多线程程序,还是用main()函数比较好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值