模拟我正在测试的同一个类中的方法调用,它真的是代码味道吗?

时间:2017-09-18 00:02:17

标签: java junit mockito

我正在尝试测试服务类(负责调用存储库层并在需要时执行一些操作),基本上,这是我正在尝试测试的类

class CarServiceImpl{
  public Car findById(String id){
      //call repository layer to find a car
  }

  public void deleteById(String id){
      Car car = this.findById(id);
      if(car != null){ 
          //Call repository layer to update the car
      }else{
          Throw NotFOundException();
      }
  }
}

正如您所看到的,我在deleteById方法上调用了findById方法,所以我的问题是。

  1. 在同一个类上调用方法真的是代码味道吗?我不认为我应该创建一个单独的类来通过id找到一辆汽车。

  2. 如果我使用Mockito.when(carServiceImpl.findById("car1")).thenReturn(carModel);,如何在“deleteById”方法上模拟对“findById”的调用 它仍然调用方法所以我需要模拟对存储库的调用以便通过id查找,即使我已经测试了方法findById。

2 个答案:

答案 0 :(得分:7)

这不一定是一种气味,你可以像这样部分模仿BroadcastReceiver receiveSMS = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { try { String smsBody = intent.getStringExtra("sms"); String pin = smsBody.replace(getResources().getString(R.string.your_extra_text), "").trim(); editText_confirm_pin.setText(pin); if (validatePin(pin)) // go through the app } catch (Exception ex) { ex.printStackTrace(); } } };

Car

,如果您允许String carId = "..."; Car car = ...; CarServiceImpl car = mock(CarServiceImpl.class); when(car.findById(carId)).thenReturn(car); when(car.deleteById(carId)).thenCallRealMethod(); 执行真正的方法'那么你的测试必须已经有一个存储库,在这种情况下让deleteById()成为一个真正的电话'很简单,并且无需额外费用即可提高测试覆盖质量。您已经测试findById()的事实并不意味着您不应该间接地再次测试它,作为findById()的一部分。

我建议你做以下其中一项或两项:

  • 单元测试deleteById()给它一个模拟Car并使用模拟的期望和验证来测试其所有方法
  • 功能/验收测试repository,为其提供一个真实的存储库,并在底层存储上使用真实的调用来断言每个方法的实际结果

另外,我猜想将存储库注入域对象的想法是故意使用"活动记录"模式,您的实体知道如何自己CRUD。 可以被视为代码气味;它违反了SRP并且可能被认为是一个不好的关注点,因为域对象知道两件事:它自己的状态以及如何坚持自己。

答案 1 :(得分:2)

您希望您的测试设置和测试代码尽可能“简约”。从这个意义上说,另一个答案是正确的说:如果你可以在没有特殊设置的情况下测试deleteById(),那就去做吧。

事实证明findById()本身就是一个需要大量测试设置的“巨大”事物 - 然后我宁愿退一步考虑将此方法的内容放入 distinct 类 - 某种CarIdentityService

含义:当我们开始使 test 代码更复杂时,通常更好的答案是退后一步并改变我们的生产代码的设计。在您的情况下,您可能希望将整个findById()代码推送到一个不同的类中 - 现在您只需模拟 CarService类中的查找器对象。

仅供记录:CarIdentityService可以是CarService的本地或内部类。但引入它可能会让您简化实施并避免陷入间谍业务。