使用Groovy进行Java的单元测试

      • 为什么用Groovy写单元测试

        它与Java 平台无缝的集成,它是基于 Java 的语言(不像其他语言,是对 JRE 的替代,因此可能基于旧版的处理器),对于 Java 开发人员来说,Groovy 意味着一条短得让人难以置信的学习曲线。而且一旦将这条学习曲线拉直,Groovy 就能提供一个无与伦比的快速开发平台。

        从这个角度来说,Groovy 成功的秘密,在于它的语法 就是 Java 语法,但是规则更少。例如,Groovy 不要求使用分号,变量类型和访问修饰符也是可选的。而且,Groovy 利用了标准的 Java 库,这些都是您已经很熟悉的,包括 Collections 和 File/IO。而且,您还可以利用任何 Groovy 提供的 Java 库,包括 JUnit。

        事实上,令人放松的类 Java 语法、对标准 Java 库的重用以及快捷的生成-运行周期,这些都使 Groovy 成为快速开发单元测试的理想替代品。



      • 如何使用Groovy进行Mock测试

        用Groovy进行Mock要比JMockit之类的简单很多,下面举例还演示如何使用

        待测试类 折叠源码
        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
        49
        50
        51
        52
        53
        54
        55
        56
        57
        @Slf4j
        public class ObjectDataController implements IObjectDataController {
          @Resource
          ObjectDescribeServiceImpl objectDescribeService;
          @Resource
          ObjectDataProxy objectDataProxy;
          @Resource
          LayoutServiceImpl layoutService;
         
          @Override
          public FcpServiceResult findById(FindObjectDataArg arg) {
            if (validateArg(arg)) {
              return FCPUtils.buildFcpServiceResult(null, Status.CODE_ERROR, Status.MESSAGE_PARAMETER_IS_NULL, null);
            }
            try {
              IObjectDescribe describe;
         
              describe = getObjectDescribeInstance(arg, arg.getObject_describe_id(), arg.getObject_describe_apiname(), Id.parse(arg.getObject_describe_id()));
              if (null == describe) {
                return FCPUtils.buildFcpServiceResult(null, Status.CODE_NOT_FOUND, Status.MESSAGE_NOT_FOUND, null);
              }
         
              IObjectData objectData = objectDataProxy.findById(Id.parse(arg.getObject_data_id()), describe.getTenantId(), describe.getApiName());
              return getFcpServiceResult(arg, describe, objectData);
            catch (Exception e) {
              log.error("find object data err. tenant_id:{},object_describe_id:{}, object_data_id:{}", arg.getEnterpriseId(), arg.getObject_describe_id(), arg.getObject_data_id(), e);
              return FCPUtils.buildFcpServiceResult(null, Status.CODE_ERROR, Status.MESSAGE_ERROR, e);
            }
          }
         
          private FcpServiceResult getFcpServiceResult(FindObjectDataArg arg, IObjectDescribe describe, IObjectData objectData) {
            if (null == objectData) {
              return FCPUtils.buildFcpServiceResult(null, Status.CODE_NOT_FOUND, Status.MESSAGE_NOT_FOUND, null);
         
            else {
              ILayout layout = null;
              if (arg.isInclude_layout()) {
                layout = layoutService.findDefault(describe.getApiName(), arg.getEnterpriseId());
                if (null == layout) {
                  return FCPUtils.buildFcpServiceResult(null, Status.CODE_NOT_FOUND, Status.MESSAGE_NOT_FOUND, null);
                }
              }
              return FCPUtils.buildFcpServiceResult(FCPResult.builder().objectDescribe(((ObjectDescribe) describe).getContainerDocument()).objectData(objectData).layout(layout != null ? ((Layout) layout).getContainerDocument() : null).build(), Status.CODE_OK, Status.MESSAGE_OK, null);
            }
          }
          
          private IObjectDescribe getObjectDescribeInstance(FindObjectDataArg arg, String object_describe_id, String object_describe_apiname, Id parse) {
            IObjectDescribe describe;
            if (Strings.isNullOrEmpty(object_describe_id)) {
              describe =
                objectDescribeService.findByTenantIdAndObjectDescribeApiName(arg.getEnterpriseId(), object_describe_apiname);
            else {
              describe = objectDescribeService.findById(arg.getEnterpriseId(), parse);
            }
            return describe;
          }
        }


        我们需要测试ObjectDataController中的findById方法, 在findById方法中用到了ObjectDescribeService, objectDataProxy和 layoutService, 我们需要把这三个实例的对应方法mock掉来测试各种返回结果下的代码处理逻辑。

        在mock这些class之前,首先需要保证ObjectDataController中有对应的三个实例的setter方法,如下代码所示

        对应的setter方法 折叠源码
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        @Override
        public void setObjectDataService(ObjectDataProxy proxy) {
          this.objectDataProxy = proxy;
        }
         
        @Override
        public void setObjectDescribeService(ObjectDescribeServiceImpl objectDescribeService) {
          this.objectDescribeService = objectDescribeService;
        }
         
        @Override
        public void setLayoutService(LayoutServiceImpl layoutService) {
          this.layoutService = layoutService;
        }

         

        实现了对应的setter方法后,我们就可以写unitTest了,最后的代码如下所示

        单元测试类 折叠源码
        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
        @RunWith(SpringJUnit4ClassRunner.class)
        @ContextConfiguration(value = "classpath:applicationContext.xml")
        class ObjectDataFCPTest {
          @Autowired
          IObjectDataController objectDataController
          
          @Test
          public void test_find_by_id_success() throws Exception {
            def data = [
              getEnterpriseId      : { -> test_tenant_1 },
              getUserId            : { -> user_id },
              getObject_data_id     : { -> id },
              getObject_describe_id :{ -> id}
            ] as FindObjectDataArg
         
            def mock = [findById: {Id id, String eid, String api -> after}] as ObjectDataProxy
            objectDataController.setObjectDataService(mock)
         
            def mockDescribe = [findById: { String tenantId, Id id -> describe },
                              findByTenantIdAndObjectDescribeApiName : {String tenantId, String apiName -> describe}] as ObjectDescribeServiceImpl
            objectDataController.setObjectDescribeService(mockDescribe)
         
            FcpServiceResult result = objectDataController.findById(data);
            FCPResult fcpResult = (FCPResult)result.getResult();
            ObjectData config = (ObjectData) fcpResult.objectData
            assertEquals(test_tenant_1, config.getTenantId())
            assertEquals(id, config.getId().strValue())
            assertEquals("example", config.getPackage())
            assertEquals("客户数据", config.getName())
            assertEquals(false, config.isDeleted())
            assertEquals(1, config.getVersion())
            assertEquals(user_id, config.getCreatedBy())
            assertEquals(user_id, config.getLastModifiedBy())
          }
        }

         

        最后的代码因为传入参数没有包含layout部分, 所以没有mock layoutService的对应方法, 整个mock过程就是这么简单。如果你想更深入的了解groovy的单元测试实现,可以了解下spock框架

posted @ 2018-04-02 14:15  酷酷的宋  阅读(702)  评论(0编辑  收藏  举报