Java单元测试框架(学习笔记)

2017/07/12 00:58
阅读数 240

概念

单元测试(unit testing),指对代码的最小可测试单元进行检查和验证

UT的目的

  • 验证代码的正确性
  • 修改后并提交是进行验证
  • 验证代码设计是否合理

什么是桩

桩代码(stub),是指用来代替关联代码或者未实现代码的代码。

如果函数B用B1来代替,那么,B称为原函数,B1称为桩函数。打桩就是编写或生成桩代码。

意图是为了让测试对象可以正常的执行,其实现一般会硬编码一些输入和输出。

 打桩的目的

  • 隔离
  • 控制
  • 补齐

 

  • 隔离,指将测试任务从产品项目中分离出来,使之能够独立编译、链接,并独立运行。隔离的基本方法就是打桩,将测试任务之外的,并且与测试任务相关的代码,用桩来代替,从而实现分离测试任务。例如函数A调用了函数B,函数B又调用了函数C和D,如果函数B用桩来代替,函数A就可以完全割断与函数C和D的关系。
  • 控制是指在测试时,人为设定相关代码的行为,使之符合测试需求。设置返回结果。
  • 补齐是指用桩来代替未实现的代码,例如,函数A调用了函数B,而函数B由其他程序员编写,且未实现,那么,可以用桩来代替函数B,使函数A能够运行并测试。补齐在并行开发中很常用。

 

模拟

什么是模拟

模拟(mock)除了保证stub的功能之外,还可深入的模拟对象之间的交互方式。

因此mock可以理解为是一种功能丰富的stub。

 

Java常用的UT框架

  • Junit3/Junit4
  • Spring mockMvc
  • EasyMock
  • Mockito

 

JUNIT

JUnit是Java单元测试框架,已经在Eclipse中默认安装。目前主流的有JUnit3和JUnit4。JUnit3中,测试用例需要继承TestCase类。JUnit4中,测试用例无需继承TestCase类,只需要使用@Test等注解。

Junit3

// 测试java.lang.Math  
// 必须继承TestCase  
public class Junit3TestCase extends TestCase {  
    public Junit3TestCase() {  
        super();  
    }  
    
        // 传入测试用例名称  
    public Junit3TestCase(String name) {  
        super(name);  
    }  
   
        // 在每个Test运行之前运行  
    @Override  
    protected void setUp() throws Exception {  
        System.out.println("Set up");  
    }  
        // 测试方法。  
        // 方法名称必须以test开头,没有参数,无返回值,是公开的,可以抛出异常  
        // 也即类似public void testXXX() throws Exception {}  
    public void testMathPow() {  
        System.out.println("Test Math.pow");  
        Assert.assertEquals(4.0, Math.pow(2.0, 2.0));  
    }  
   
    public void testMathMin() {  
        System.out.println("Test Math.min");  
        Assert.assertEquals(2.0, Math.min(2.0, 4.0));  
    }  
   
        // 在每个Test运行之后运行  
    @Override  
    protected void tearDown() throws Exception {  
        System.out.println("Tear down");  
    }  
}  

 

运行测试方法

在Eclipse中,可以直接在类名或测试方法上右击,在弹出的右击菜单中选择Run As -> JUnit Test。
在Mvn中,可以直接通过mvn test命令运行测试用例。
也可以通过Java方式调用,创建一个TestCase实例,然后重载runTest()方法,在其方法内调用测试方法(可以多个)。

TestCase test = new Junit3TestCase("mathPow") {  
        // 重载  
    protected void runTest() throws Throwable {  
        testMathPow();  
    };  
};  
test.run();  

 

更加便捷地,可以在创建TestCase实例时直接传入测试方法名称,JUnit会自动调用此测试方法,如

TestCase test = new Junit3TestCase("testMathPow");  
test.run();  

 

Junit TestSuite

TestSuite是测试用例套件,能够运行过个测试方法。如果不指定TestSuite,会创建一个默认的TestSuite。默认TestSuite会扫描当前内中的所有测试方法,然后运行。
如果不想采用默认的TestSuite,则可以自定义TestSuite。在TestCase中,可以通过静态方法suite()返回自定义的suite。

import junit.framework.Assert;  
import junit.framework.Test;  
import junit.framework.TestCase;  
import junit.framework.TestSuite;  
   
public class Junit3TestCase extends TestCase {  
        //...  
    public static Test suite() {  
        System.out.println("create suite");  
        TestSuite suite = new TestSuite();  
        suite.addTest(new Junit3TestCase("testMathPow"));  
        return suite;  
    }  
}  

 

JUnit4

JUnit4通过注解的方式来识别测试方法。目前支持的主要注解有:

  • @BeforeClass 全局只会执行一次,而且是第一个运行
  • @Before 在测试方法运行之前运行
  • @Test 测试方法
  • @After 在测试方法运行之后允许
  • @AfterClass 全局只会执行一次,而且是最后一个运行
  • @Ignore 忽略此方法

执行次序是@BeforeClass -> @Before -> @Test -> @After -> @Before -> @Test -> @After -> @AfterClass@Ignore会被忽略。

 

Assert

Junit3和Junit4都提供了一个Assert类(虽然package不同,但是大致差不多)。Assert类中定义了很多静态方法来进行断言。列表如下:

  • assertTrue(String message, boolean condition) 要求condition == true
  • assertFalse(String message, boolean condition) 要求condition == false
  • fail(String message) 必然失败,同样要求代码不可达
  • assertEquals(String message, XXX expected,XXX actual) 要求expected.equals(actual)
  • assertArrayEquals(String message, XXX[] expecteds,XXX [] actuals) 要求expected.equalsArray(actual)
  • assertNotNull(String message, Object object) 要求object!=null
  • assertNull(String message, Object object) 要求object==null
  • assertSame(String message, Object expected, Object actual) 要求expected == actual
  • assertNotSame(String message, Object unexpected,Object actual) 要求expected != actual
  • assertThat(String reason, T actual, Matcher matcher) 要求matcher.matches(actual) == true

 

Spring中用MockMvc

MockMvcBuilder

MockMvcBuilder是用来构造MockMvc的构造器,其主要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder,分别对应两种测试方式,即独立安装和集成Web环境测试(此种方式并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)。对于我们来说直接使用静态工厂MockMvcBuilders创建即可。

MockMvcBuilders.webAppContextSetup(WebApplicationContext context):指定WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc;

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration("classpath:config/IncotermsRestServiceTest-context.xml")  
@WebAppConfiguration  
public class IncotermsRestServiceTest {  
    @Autowired  
    private WebApplicationContext wac;  
    private MockMvc mockMvc;  
    @Before  
    public void setup() {  
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();   //构造MockMvc  
    }  
    ...  
}  

 

一些常用的测试

 

得到MvcResult自定义验证

MvcResult result = mockMvc.perform(get("/user/{id}", 1))//执行请求    
        .andReturn(); //返回MvcResult    
Assert.assertNotNull(result.getModelAndView().getModel().get("user")); //自定义断言  

 

文件上传

byte[] bytes = new byte[] {1, 2};    
mockMvc.perform(fileUpload("/user/{id}/icon", 1L).file("icon", bytes)) //执行文件上传    
        .andExpect(model().attribute("icon", bytes)) //验证属性相等性    
        .andExpect(view().name("success")); //验证视图    

 

JSON请求/响应验证

String requestBody = "{\"id\":1, \"name\":\"zhang\"}";    
    mockMvc.perform(post("/user")    
            .contentType(MediaType.APPLICATION_JSON).content(requestBody)    
            .accept(MediaType.APPLICATION_JSON)) //执行请求    
            .andExpect(content().contentType(MediaType.APPLICATION_JSON)) //验证响应contentType    
            .andExpect(jsonPath("$.id").value(1)); //使用Json path验证JSON 请参考http://goessner.net/articles/JsonPath/    
        
    String errorBody = "{id:1, name:zhang}";    
    MvcResult result = mockMvc.perform(post("/user")    
            .contentType(MediaType.APPLICATION_JSON).content(errorBody)    
            .accept(MediaType.APPLICATION_JSON)) //执行请求    
            .andExpect(status().isBadRequest()) //400错误请求    
            .andReturn();    
        
    Assert.assertTrue(HttpMessageNotReadableException.class.isAssignableFrom(result.getResolvedException().getClass()));//错误的请求内容体  

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
1 收藏
1
分享
返回顶部
顶部