zoukankan      html  css  js  c++  java
  • 两个基于spring的单元测试简单样例

      单元测试,从一定程度上可以看出一个同学达到的层次。但又不完全是,有时可能只是一个思考方式的转变。单元测试有非常多的工具供选择,在java中,junit无疑是比较常用的。本文列出,junit在spring中的使用样例,供参考。

    1. 单元测试主要方式

      这里仅说我们常用的单元测试的场景,或者是我自己常用的场景,主要分为4大类:

        1. 对外提供的接口级别的测试,如rest-api, 主要用于保证对外提供的接口符合预期, 而非等到别人调用时才发现异常; 

        2. serivce 级别的单元测试, 主要用于保证service功能正常;

        3. 静态方法的测试, 主要用于测试一些工具类符合预期,这类测试一般比较简单;

        4. mock接口实现测试, 这类测试往往最复杂, 一般是为测试复杂场景, 但又要保证影响因素单一, 保证测试的有效性, 要求既要mock得少也要求mock得合适, 最难;

          一般还有对环境初始化时的运行,和结束测试时的清理工作,即setup() 和teardow(). 在junit中就体现为两个注解:@Before 和 @After 。

      实际上,除了最后一种测试是比较体系化和完备的之外,前几种也许都不是那么细致,至少一般测试不到某个很小的点上,或者说场景不一致。api,service一般会涉及到复杂的外部系统调用,一是依赖多二是速度慢,而尽量保持本地化测试中一个最佳实践。但记住一点,单元测试应该基于行为,而非基于实现。

    2. springmvc 的单元测试样例

      这里主要说的是低版本的springmvc, 里面依赖还比较原始, 所以需要单独讲讲。其依赖包可如下参考:

            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.hamcrest</groupId>
                <artifactId>hamcrest-core</artifactId>
                <version>1.3</version>
            </dependency>
            <dependency>
                <groupId>org.hamcrest</groupId>
                <artifactId>hamcrest-library</artifactId>
                <version>1.3</version>
            </dependency>
            <dependency>
                <groupId>org.mockito</groupId>
                <artifactId>mockito-all</artifactId>
                <version>1.9.5</version>
                <scope>test</scope>
            </dependency>

      测试用例样例如下:(主要注意必要时引用 servlet的配置就行,否则可能找不到对应的controller)

    @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration
    @ContextConfiguration({"classpath:applicationContext.xml",
                            "classpath:applicationContext-servlet.xml"})
    public class SpringMvcTest {
    
        @Autowired
        private WebApplicationContext wac;
    
        private MockMvc mockMvc;
    
        @Before
        public void before() throws Exception {
            mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
        }
    
        // 对外提供的接口级别的测试 @Before  @After
        @Test
        public void tesaDataAddKeyword() throws Exception {
            TestObj tobj;
            MvcResult mvcResult;
            String responseResult;
            JSONObject resultObj;
            tobj = new TestObj();
            tobj.setXX(302L);
            mvcResult = mockMvc.perform(
                    MockMvcRequestBuilders.post("/test/add")
                            .contentType("application/json")
                            .characterEncoding("utf-8")
                            .content(JSONObject.toJSONString(tobj)))
                    .andExpect(MockMvcResultMatchers.status().is(200))
                    .andReturn();
            responseResult = mvcResult.getResponse().getContentAsString();
            System.out.println("接口返回结果:" + responseResult);
            resultObj = JSONObject.parseObject(responseResult);
            Assert.assertNotNull("响应结果为空", resultObj);
            Assert.assertThat("正常插入失败",
                    resultObj.getInteger("status"), is(1));
    
        }
    
        
        @Resource
        private TestService testService;
        
        // serivce 级别的单元测试
        @Test
        public void testPureKeyword() {
            TestObj tObj = new TestObj();
            tObj.setXXX(302L);
            try {
                testService.checkKeyWord(tObj);
            }
            catch (BizException e) {
                Assert.assertEquals("错误码返回不正确", 4001021, e.getErrCode());
            }
        }
    
        // 静态方法的测试
        @Test
        public void testUiExtract() {
            String ruleEL;
            ParsedClause parsedClause;
    
            ruleEL = "ui_extract($123, 'md') = '02-01'";
    
            parsedClause = SyntaxParser.parse(ruleEL);
    
            Assert.assertEquals("数量解析不正确",
                    1, parsedClause.getLabelIdMapping().size());
            Assert.assertEquals("解析UPP结果不正确",
                    "string.substring($123 , 5, 10) = '02-01'",
                    parsedClause.translateTo(DialectTypeEnum.ES));
            Assert.assertEquals("解析结果不正确",
                    "substr($123 , 5, 5) = '02-01'",
                    parsedClause.translateTo(DialectTypeEnum.HIVE));
    
        }
    
        // mock接口实现测试
        @Test
        public void testMockInterface() {
    
            List mockList = Mockito.mock(List.class);
            mockList.add("1");
            // 返回null,说明并没有调用真正的方法
            Assert.assertNull("mock没有返回null", mockList.get(0));
            Mockito.when(mockList.size()).thenReturn(100);//stub
            // size() method was stubbed,返回100
            Assert.assertThat("mock.size未返回预期值",
                        mockList.size(), is(100));
    
            //test for Spy
            List list = new LinkedList();
            List spy = Mockito.spy(list);
    
            //optionally, you can stub out some methods:
            Mockito.when(spy.size()).thenReturn(100);
    
            //using the spy calls real methods
            spy.add("one");
            spy.add("two");
    
            //prints "one" - the first element of a list
            System.out.println(spy.get(0));
    
            //size() method was stubbed - 100 is printed
            System.out.println(spy.size());
        }
    
        // 预期发生异常的场景测试
        @Test(expected = BizException.class)
        public void testMethodThrow() {
            SyntaxParser.parse(null);
        }
    
    }

      即对上面4种场景的简单实现样例。

    3. springboot的单元测试样例

      springboot为我们省去了许多的依赖问题,所以不会很麻烦。只需引入 test 包,其他相应依赖就下来了。而且一般都是demo代码里默认带有的依赖:

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>

      其使用样例则也会更简单,一个注解搞定了。

    @RunWith(SpringRunner.class)
    @SpringBootTest
    @AutoConfigureMockMvc
    public class DemoBatchDataControllerTest {
    
        @Autowired
        private MockMvc mockMvc;
    
        @Test
        public void testApi1() throws Exception {
            MvcResult result = mockMvc.perform(get("/demo/test1"))
                                .andExpect(status().isOk())
                                .andReturn();
            Assert.assertThat("结果不正确", 
                    result.getResponse().getContentAsString(), containsString("ok"));
        }
    
        // 其他同springmvc
    
    
    }

      可见springboot确实简单了许多。但框架始终只是框架,需要用户注入灵魂,才能在其上面玩出花样来。

    4. junit常用断言枚举

          测试有个非常重要的属性就是,有预期。当预期结果不一致时,则用例失败。即断言判定。种类实际也不多,罗列如下:

    // 断言两个对象是否相等
    assertEquals
    // 断言两个数组是否包含相同的元素
    assertArrayEquals
    // 断言语句为真
    assertTrue
    // 断言语句为假
    assertFalse
    // 断言对象引用为空
    assertNull
    // 断言对象引用不为空
    assertNotNull
    // 断言两个对象指向同一对象,即同对象
    assertSame
    // 断言两个对象不是指同一个
    assertNotSame
    // 断言对象满足某个条件(自定义实现,有部分现成工具类供调用)
    assertThat
    // 定义符合预期的异常
    @Test(expect = xxxException.class)
    

          基本上,通过以上断言,就可以将测试预判做好了。但也不那么简单。

      测试驱动或者测试先行开发,是一种比较好的实践,可以让我们少走弯路,且更自信。

    不要害怕今日的苦,你要相信明天,更苦!
  • 相关阅读:
    C#操作json
    sql server 2008 身份验证失败 18456
    MD5密码加密
    oracle dg 报错提示 涉及硬盘错误
    Rhel6.5 相关操作
    Centos6.9部署vnc
    Sqluldr2 libclntsh.so报错处理
    时钟服务器同步方法
    windows copy 和xcopy
    Linux 本地repo配置
  • 原文地址:https://www.cnblogs.com/yougewe/p/14540105.html
Copyright © 2011-2022 走看看