Junit开发指南

基类

com.enation.app.javashop.framework.test.BaseTest

此类加入了基础的注解:

@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
@SpringBootTest()
@ComponentScan("com.enation.app.javashop")
@Transactional
@Rollback()
public  abstract class BaseTest {
}

基类加入了事务和自动回滚的注解,那么在单无测试中相应的操作最终最会呗回滚,而不影响下次测试

bootstrap.yml

需要在src/main/test/resources中定义一个bootstrap.yml,他和main/resources中的只有一项profile不一样:

spring:
  application:
    name: admin-api
  cloud:
    config:
      uri: http://localhost:8888
      label: master
      profile: test
server:
  port: 8082

druid的配置

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/v7_goods
    username: root
    password: kingapex
    driver-class-name: com.mysql.jdbc.Driver
    druid:
      web-stat-filter:
        enabled: false

因druid的bug,使用 @AutoConfigureMockMvc 会导致他的一个filter报空指针异常,所以在我们的test 配置中要增加的配置:

spring.datasource.druid.web-stat-filter.enabled=false

来禁用druid的filter

tocken权限

在BaseTest中,定义了seller1,seller2,buyer1,buyer2四个tocken值,不需要在自己的单元测试中重复定义:

token uid sellerid
seller1 3
seller2 4
buyer1 1
buyer2 2

示例

    @Test
    public void testAdd(){
        Map brand  = new HashMap();
        brand.put("name","nametest");
        brand.put("logo","logotest");

        //执行插入的操作
        daoSupport.insert("es_brand",brand);

        //在此事务中,插入的数据可见
        String dblogo =  daoSupport.queryForString("select logo from es_brand where name=?","nametest");

        //断言中也证明事务可见
        Assert.assertEquals(dblogo,"logotest");

        //测试结束后,会回滚测试中对数据库的操作
    }

多数据源事务

我们系统因分库存在多个数据源,那么默认的事务管理是对商品库的事务管理,如果你的单元测试需要事务回滚,需要在自己的测试类上声明自己的事务管理,比如系统相关的单元测试如果要回滚:

@Transactional(value = "systemTransactionManager",rollbackFor = Exception.class)
public class SettingManagerTest extends BaseTest{
   ...

事务的周期

值得注意的是,每个单元测试(即每个@Test)都是一个事务周期

public class MyTest extends BaseTest {
    @Test
    public void testA(){ }

    @Test
    public void testB(){ }
}

如上所示,testA中对数据的操作,在testB中是不可见的,如果有要重复利用的操作,可以在testB中调用testA:

public class MyTest extends BaseTest {
    @Test
    public void testA(){ }

    @Test
    public void testB(){ testA(); }
}

当然根据你的业务场景需要,可以在自己的单元测试上注解@Rollback(false),来禁用回滚:

@Rollback(false)
public class MyTest extends BaseTest {
 ...
}

测试数据的准备和清除

如果测试场景需要,可以通过@Before、@After 注解来构造、清除测试所需数据:

    @Before
    public void insertTestData(){        
    }

    @After
    public void cleanTestData(){
    }

对象比较断言

为了方便restful api的单元测试,在BaseTest中内置了一个对象比较 ResultMatcher,使用方法如下:

    @Test
    public void testAdd() throws Exception {

        //构建一个预期的对象
        Brand brand  = new Brand();
        brand.setName("name1");
        brand.setLog("logo1");

        //在断言中直接使用 objectEquals 
        mockMvc.perform(post("/goods/brands").param(...)
                .andExpect(  objectEquals( brand ) );

    }

说明

objectEquals 比较的是controller中的返回值是否和预期的一样

实际上是将response的body(json)转为对象,再和预期的对象进行比较

这就要求要比较的对象必须实现了equals方法和toString方法

参数的批量校验

在BaseTest中提供了一个构建多参数map的方法 toMultiValueMaps ,使用示例:

    @Test
    public void testAddParams() throws Exception {

        //定义参数名
        String[] names = new String[]{"name","logo","message"};

        //定义几组要测试的参数情况和提示信息断言message
        String[] values1 = new String[]{"","http:www.baidu.com","品牌名称不能为空"};
        String[] values2 = new String[]{"三只松鼠","","品牌图标不能为空"};

        List<MultiValueMap<String, String>> list = toMultiValueMaps(names,values2,values1);
        for (MultiValueMap<String,String> params  : list){
            String message =  params.get("message").get(0);
            ErrorMessage error  = new ErrorMessage("004",message);
            mockMvc.perform(post("/goods/brands")
                    .params( params ))
                    .andExpect(status().is(400))
                    .andExpect(  objectEquals( error ));
        }

    }

当测试参数校验很多的时候,重要的是集中精力排列下面的参数及提示的message:

        String[] names = new String[]{"name","logo","message"};
        String[] values1 = new String[]{"","http:www.baidu.com","品牌名称不能为空"};
        String[] values2 = new String[]{"三只松鼠","","品牌图标不能为空"};

consumer的测试

consumer的单元测试不用面向消息测试,直接面向业务测试即可,举例说明,假设有如下消费者:

/**
订单状态变化时发送短信
**/
@Component
public class OrderSmsMsgSender implements OrderStatusChangeEvent {

    @Override
    public void orderChange(OrderStatusChangeMsg orderMessage) {
             //发送短信的代码
    }

}

则直接调用此类来测试即可:

public class OrderSmsMsgSenderTest extends BaseTest{

    @Autowire
    private OrderSmsMsgSender orderSmsMsgSender
    @Test
    public void testSendSms(){
        OrderStatusChangeMsg msg = new OrderStatusChangeMsg();
        msg.setOrdersn("xxxx")
        orderSmsMsgSender.orderChange(msg);

        //执行断言,略...
    }

}

规范

  1. 测试类的包名,和相应的测试目标的类相同。
  2. 测试的结果靠断言,而非system.print+肉眼
  3. 单元测试中不允许出现控制台的输出(system.print)
  4. 所有controller都必须提供单元测试
  5. 复杂逻辑的业务类也要提供单元测试
  6. 在代码请求合并之前,必须保证所有的单元测试通过(包括其他人的)

results matching ""

    No results matching ""