• junit学习一


    一、简介

    这个估计大家都比我清楚了,JUnit是一个单元测试框架,我们可以基于它编写用来测试代码的代码,从而更方便地进行回归测试。

    二、编写测试与断言(Assertion)

    在Junit4中,编写一个测试方法只需要使用@Test注解并保证被注解的方法满足以下条件

    • 方法可见性为public
    • 方法无返回值
    • 方法没有参数

    在一个测试中,往往需要满足某种条件才能断定测试成功,而不仅仅是测试方法执行完毕,org.junit.Assert对象提供了各种断言方法,用于判定程序的执行结果是否符合预期,从而通过测试。

    例如我们需要测试以下类的两个方法:

     1 package org.haibin369.common;  
     2   
     3 public class ObjectGenerator {  
     4     public String getString(){  
     5         return "String";  
     6     }  
     7   
     8     public Object getNull(){  
     9         return null;  
    10     }  
    11 }  

    我们需要编写以下的测试类和方法:

     1 package org.haibin369.test;  
     2   
     3 import org.haibin369.common.ObjectGenerator;  
     4 import org.junit.Test;  
     5   
     6 //静态导入,方便使用Assert对象的断言方法  
     7 import static org.junit.Assert.*;  
     8   
     9 /** 
    10  * 测试类,不需继承任何JUnit的类 
    11  */  
    12 public class ObjectGeneratorTest {  
    13     //使用@Test标注测试方法  
    14     @Test  
    15     public void testGetString() {  
    16         ObjectGenerator generator = new ObjectGenerator();  
    17         String msg = generator.getString();  
    18         if (msg == null) {  
    19             //Assert中也有使测试失败的fail方法,参数为失败信息(此处仅作演示)  
    20             fail("Message is null");  
    21         }  
    22   
    23         //断言得到的msg为AString,否则测试失败,第一个参数为失败时的信息  
    24         assertEquals("Wrong message generated.", "AString", msg);  
    25     }  
    26   
    27     @Test  
    28     public void testGetNull() {  
    29         ObjectGenerator generator = new ObjectGenerator();  
    30         //断言为空  
    31         assertNull("Returned object is not null", generator.getNull());  
    32     }  
    33 }  

    执行以上测试,第二个测试会通过,而第一个会报错(org.junit.ComparisonFailure: Wrong message generated.),表明代码返回的结果和预期的不一样。

    org.junit.Assert对象中还有很多断言方法,详情可参考API。

     三、Before & After

     现在有一个简单的登陆Action需要测试(User和ACLException代码比较简单,这里就不贴出来了)。

    public class LoginAction {  
        private static final User FORBIDDEN_USER = new User("admin", "admin");  
        private static final List<User> LOGIN_USER = new ArrayList<User>();  
      
        public void login(User user) throws ACLException, InterruptedException {  
            if (FORBIDDEN_USER.equals(user)) {  
                Thread.sleep(2000);  
                throw new ACLException("Access Denied!");  
            }  
      
            if (!LOGIN_USER.contains(user)) {  
                LOGIN_USER.add(user);  
            }  
        }  
      
        public void logout(User user) throws InterruptedException {  
            LOGIN_USER.remove(user);  
        }  
      
        public List<User> getLoginUser() {  
            return LOGIN_USER;  
        }  
    }  
    

      

     测试类很简单,如下:

     1 public class LoginActionTest {  
     2     @Test  
     3     public void testLoginSuccess() throws Exception {  
     4         LoginAction loginAction = new LoginAction();  
     5         User user = new User("haibin369", "123456");  
     6         loginAction.login(user);  
     7   
     8         assertTrue("User didn't login!", loginAction.getLoginUser().contains(user));  
     9     }  
    10   
    11     @Test  
    12     public void testLogout() throws Exception {  
    13         LoginAction loginAction = new LoginAction();  
    14         User user = new User("haibin369", "123456");  
    15         loginAction.login(user);  
    16         loginAction.logout(user);  
    17   
    18         assertFalse("User didn't logout!", loginAction.getLoginUser().contains(user));  
    19     }  
    20 }  

    问题是这些测试中都有重复的代码去创建LoginAction,所以可以考虑把LoginAction作为成员变量,只初始化一次。同时为了避免测试方法间的影响,可以在每个测试执行完之后重置LoginAction的状态,即清空LOGIN_USER。在一般的测试中也许也会有类似的需求:在测试开始时打开一个文件,所有测试结束之后关闭这个文件。为了实现这种目的,JUnit提供了以下四个方法注解实现这种目的:

    • @BeforeClass / @AfterClass:在所有测试方法执行之前 / 后执行,被注解的方法必须是public,static,无返回值,无参数;
    • @Before / @After:在每个测试方法执行之前 / 后执行,被注解的方法必须是public,无返回值,无参数;

    重写后的测试如下:

     1 public class LoginActionTest {  
     2     private static LoginAction loginAction;  
     3     private static User user;  
     4   
     5     @BeforeClass  
     6     public static void init() {  
     7         loginAction = new LoginAction();  
     8         user = new User("haibin369", "123456");  
     9     }  
    10   
    11     @After  
    12     public void clearLoginUser() {  
    13         loginAction.getLoginUser().clear();  
    14     }  
    15   
    16     @Test  
    17     public void testLoginSuccess() throws Exception {  
    18         loginAction.login(user);  
    19   
    20         assertTrue("User didn't login!", loginAction.getLoginUser().contains(user));  
    21     }  
    22   
    23     @Test  
    24     public void testLogout() throws Exception {  
    25         loginAction.login(user);  
    26         loginAction.logout(user);  
    27   
    28         assertFalse("User didn't logout!", loginAction.getLoginUser().contains(user));  
    29     }  
    30 }  

     四、异常测试

     在上面的LoginAction中,当使用Admin帐号登陆时,会抛出异常,这部分代码也需要测试,我们可以在@Test注解中配置期待异常,当测试抛出指定异常的时候则测试成功。

    1 //当测试方法抛出ACLException时测试成功  
    2 @Test(expected = ACLException.class)  
    3 public void testAdminLogin() throws ACLException, InterruptedException {  
    4     loginAction.login(new User("admin", "admin"));  
    5 }  

     上面的测试能测试出方法按照预期抛出异常,但是如果代码里面不只一个地方抛出ACLException(只是包含的信息不一样),我们还是无法分辨出来。这种情况可以使用JUnit的ExpectedException Rule来解决。

     1 //使用@Rule标记ExpectedException  
     2 @Rule  
     3 public ExpectedException expectedException = ExpectedException.none();  
     4   
     5 @Test  
     6 public void testAdminLogin2() throws ACLException, InterruptedException {  
     7     //期待抛出ACLException  
     8     expectedException.expect(ACLException.class);  
     9     //期待抛出的异常信息中包含"Access Denied"字符串  
    10     expectedException.expectMessage(CoreMatchers.containsString("Access Denied"));  
    11     //当然也可以直接传入字符串,表示期待的异常信息(完全匹配)  
    12     //expectedException.expectMessage("Access Denied!");  
    13       
    14     loginAction.login(new User("admin", "admin"));  
    15 }  

    五、超时测试

    在JUnit中测试超时是使用@Test的timeout属性设置

    1 //设置1000ms的超时时间,当超过这个时间测试还没执行完毕则失败  
    2 @Test(timeout = 1000)  
    3 public void testLoginTimeout() throws Exception {  
    4     loginAction.login(new User("admin", "admin"));  
    5 }  

     也可以使用Timeout Rule设定全局的超时时间

    1 //设置1000ms的超时时间,当超过这个时间测试还没执行完毕则失败  
    2 @Rule  
    3 public Timeout timeout = new Timeout(1000);  
    4   
    5 @Test  
    6 public void testLoginTimeout() throws Exception {  
    7     loginAction.login(new User("admin", "admin"));  
    8 }  

     上面两个测试执行都会失败:java.lang.Exception: test timed out after 1000 milliseconds

    六、忽略测试

    使用@Ignore可以忽略一个测试

    1 //忽略该测试,参数为输出信息  
    2 @Ignore("Temporary ignored as no changes.")  
    3 @Test(timeout = 1000)  
    4 public void testLoginTimeout() throws Exception {  
    5     loginAction.login(new User("admin", "admin"));  
    6 }  

     执行类里的所有测试,会输出一下信息,表示该测试被忽略了。

    Test 'org.haibin369.test.LoginActionTest.testLoginTimeout' ignored (Temporary ignored as no changes.)

    七、使用Suite执行多个测试类

    现在我们有了ObjectGeneratorTest和LoginActionTest,如果需要一次过执行多个测试类的所有方法,可以使用@Suite与@Suite.SuiteClasses注解

     1 //使用JUnit的Suite Runner执行测试  
     2 @RunWith(Suite.class)  
     3 //配置所有需要执行的测试  
     4 @Suite.SuiteClasses({  
     5         ObjectGeneratorTest.class,  
     6         LoginActionTest.class  
     7 })  
     8   
     9 //创建一个类作为Test Suite的入口  
    10 public class MyTestSuite {  
    11 }  

    此文为转载的日志,如有版权限制,愿意自动放弃

  • 相关阅读:
    css--盒子模型
    目标爬取社会信用码
    KFC-位置分页爬虫
    百度翻译-爬虫
    网页采集器-UA伪装
    python模块2
    python模块
    go入门
    python垃圾回收机制
    Python高级用法
  • 原文地址:https://www.cnblogs.com/johnson-blog/p/3890495.html
Copyright © 2020-2023  润新知