1

我有这个实体 -

public class MerchantConfig {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private Long merchantId;
    private String configKey;
    private String configValue;
    ..
}

它目前在商家级别存储配置(键和值)。

我们现在也需要在用户级别(用户到商家的一对多映射)存储配置。

我们将这两列添加到实体中 -

private Long entityId;
private String entityType;

entityId 现在将存储merchantId/UserId,entityType 将与字符串值“merchantid”或“user_id”指示相同。

这是一个现有的服务层方法 -

public MerchantConfig enableSync(boolean isActive, Long merchantId,boolean isSync){
        MerchantConfig merchantConfig= merchantConfigRespository.findByMerchantIdAndConfigKey(merchantId,Constants.MerchantConfigKeys.SYNC_ENABLED.key);
        if(merchantConfig==null){
            merchantConfig = new MerchantConfig();
            merchantConfig.setMerchantId(merchantId);
            merchantConfig.setConfigKey(Constants.MerchantConfigKeys.SYNC_ENABLED.key);
        }
        if(isSync)
            merchantConfig.setConfigValue("1");
        else
            merchantConfig.setConfigValue("0");
        merchantConfig.setIsActive(isActive);
        return merchantConfigRespository.save(merchantConfig);
    }

这是它上面的控制器 -

@PostMapping("/admin/enableSync")
public ResponseEntity<Object> enableSync(HttpServletRequest request, HttpServletResponse response,
                                                  @RequestParam("merchantId") Long merchantId,
                                                  @RequestParam("isValid") boolean isValid,
                                                  @RequestParam("isSync") boolean isSync) {
    if (isValid && isSync)
        LOGGER.info("enabling sync for merchant {}", merchantId);
    else
        LOGGER.info("disabling sync for merchant {}", merchantId);

    MerchantConfig merchantConfig = merchantConfigService.enableSync(isValid, merchantId, isSync);
    if(merchantConfig!=null)
        return new ResponseEntity<>(new ResponseDTO(Constants.API_RESPONSE_SUCCESS, merchantConfig, "success"),
                HttpStatus.OK);
    return new ResponseEntity<>(new ResponseDTO(Constants.API_RESPONSE_FAILURE, "unable to enable sync"),
            HttpStatus.OK);
}

我们决定只改变服务和向下,保持控制器层不变。

用服务方法中的 findByEntityIdAndEntityTypeAndConfigKey() 替换存储库方法调用 findByMerchantIdAndConfigKey() 并进行此更改 -

public MerchantConfig enableSync(boolean isActive, Long merchantId,boolean isSync){
    MerchantConfig merchantConfig= merchantConfigRespository.findByEntityIdAndEntityTypeAndConfigKey(merchantId,Enum.MerchantConfigType.MERCHANT_ID.v,Constants.MerchantConfigKeys.SYNC_ENABLED.key);
    if(merchantConfig==null){
        merchantConfig = new MerchantConfig();
        merchantConfig.setEntityId(merchantId);
        merchantConfig.setEntityType(Enum.MerchantConfigType.MERCHANT_ID.v);
        merchantConfig.setConfigKey(Constants.MerchantConfigKeys.SYNC_ENABLED.key);
    }
    if(isSync)
        merchantConfig.setConfigValue("1");
    else
        merchantConfig.setConfigValue("0");
    merchantConfig.setIsActive(isActive);
    return merchantConfigRespository.save(merchantConfig);
}

我在服务层放了什么有用的单元测试用例?

如果我要模拟存储库层响应并验证 servive 方法是否返回与模拟响应相同的 MerchantConfig 实体,这不是矫枉过正吗?

相反,我觉得测试 db 值会更有用,方法是在 db 中创建一些已知条目。例如,在这种情况下,当 db 中已经有一些商家级别的配置并且要输入用户级别的配置时,应该覆盖用户级别的条目,因为配置器正在尝试取消设置所有商家级别的配置。

如果在 db 中给定商家级别的配置,测试会很有用,当调用 save config API 端点来保存用户级别的配置时,商家级别的配置被标记为非活动状态。

顺便问一下,这些测试叫什么?请给出一些解决方案,因为我已经在这场辩论中苦苦挣扎了一段时间。每次坐下来写传统的单元测试用例,我都花太多时间,还是有问题,这是单元测试无法覆盖的。请提供一些全面的指南来编写此类测试。

4

1 回答 1

0

首先感谢提问!这是一个很好的问题,让我思考。现在关于答案...

如果有任何科学证明是编写测试的唯一正确方法,我不知道。作为专业人士,您将不得不拨打电话。如果我是你,我会考虑以下几点:

  • 为几行更改编写单元测试确实看起来有点矫枉过正。很容易想象一个人最终是如何完成 30 行的单元测试,并通过模拟来测试几行更改。让它成为选项1。
  • 另一方面,使用 DB 编写集成测试(为了测试,可能在内存中)现在看起来可能是一个简单的解决方案。让它成为选项2。

选项 1 我使用的经验法则是,如果某些东西可以通过单元测试进行测试,那么它应该通过单元测试进行测试,除非我有充分的理由不这么认为。这是我个人的选择,原因如下:

  • 速度。单元测试总是更快地运行和调试(即使它们编写的时间更长)
  • 粒度。通过单元测试,我几乎总能知道问题出在哪里。

选项 2 现在,在我看来,使用集成测试而不是单元测试的唯一正当理由是某些东西非常难以/不可能以其他方式进行测试。只有当我完全确定这部分逻辑永远不会再改变时,我可能会在你的情况下选择这个,但我认为情况并非如此。

现在我们可以提出选项 3:它确实看起来enableSync对很多事情都有效(即创建/查找对象并切换同步标志)

如果您将下面的代码提取到一个单独的方法中(比如说, build merchantConfig):

 MerchantConfig merchantConfig= 
    if(merchantConfig==null){
        merchantConfig = new MerchantConfig();
        merchantConfig.setEntityId(merchantId);
        merchantConfig.setEntityType(Enum.MerchantConfigType.MERCHANT_ID.v);
        merchantConfig.setConfigKey(Constants.MerchantConfigKeys.SYNC_ENABLED.key);
    }

这应该很容易进行单元测试,并且不需要数据库交互或模拟。

于 2020-07-20T17:26:11.283 回答