@ReplaceWithMock和@Qualifier

时间:2014-09-25 07:04:45

标签: java unit-testing spring-mvc integration-testing springockito

我正在使用springockito-annotations 1.0.9进行集成测试。

我有以下控制器:

@Autowired
    public Controller(
            @Qualifier("passwordService ") PasswordService passwordService ,
            @Qualifier("validator") Validator validator,
            @Qualifier("reportService") ReportService reportService,
            DateCalculator dateCalculator,
            Accessor accessor){
        this.passwordService = passwordService;
        this.validator = validator;
        this.reportService = reportService;
        this.dateCalculator = dateCalculator;
        this.accessor = accessor;
    }

在测试中,我将使用 @ReplaceWithMock 注释从上下文中替换bean。

但不幸的是,它仅适用于依赖 whithout @Qualifier 注释。

即,我的测试看起来像这样:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(loader = SpringockitoAnnotatedContextLoader.class, classes = {TestContext.class})
public class ControllerTest {

    @Autowired
    @ReplaceWithMock
    private PasswordService passwordService ;
    @Autowired
    @ReplaceWithMock
    private Validator validator;
    @Autowired
    @ReplaceWithMock
    private ReportService reportService;
    @Autowired
    @ReplaceWithMock
    private DateCalculator dateCalculator;
    @Autowired
    @ReplaceWithMock
    private Accessor accessor;

    @Autowired
    private Controller controller;

}

在初始化上下文后的最后一种情况下, DateCalculator和Accessor bean正确替换了所需的模拟,但另一个bean自动装配为主要上下文中的普通bean

调试后我发现 QualifierAnnotationAutowireCandidateResolver 无法正确识别bean。在下面的行中,从 229

开始
RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();
AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());

Spring试图从模拟依赖中提取限定符,但它是空的。

很高兴知道如何正确地将@Qualifier的依赖替换为模拟对象。

2 个答案:

答案 0 :(得分:2)

编辑添加了指向whitebox替代品的链接,它在Mockito的更高版本中消失了

可以使用mockitos @Mock@InjectMocks将您想要测试的内容注入到您的课程中,如其他帖子所示。我曾经认为这是测试Spring托管bean的好方法,但现在我觉得它有问题;如果 由@InjectMocks完成的注射失败,它会默默地执行,您不知道为什么。在创建测试时,它可以是可管理的,但是当您进行这样的测试时,由于无意识的小故障,几个测试开始因为无效指针而失败 更改为某人制作的应用程序上下文,或者在引入次要异常或类似的合并之后,它会变得比它需要的更加混乱。

我建议您使用mockitos Whitebox,请参阅下面的示例。有了它,你可以明确告诉你想要的字段"注入"什么对象,在复杂的情况下,你可以注入多个对象。 Mockito在注入时使用Whitebox(但吞下所有例外)。

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration( /*something that fits your setup*/ )
public class ControllerTest {
    @Autowired
    private PasswordService passwordService ;
    @Autowired
    private Validator validator;
    @Autowired
    private ReportService reportService;



    @Autowired
    private Controller testObject;

    @Before
    public void setupBefore() {

        //Since this "injection" is done manually the qualifiers does not matter
        Whitebox.setInternalState(testObject, "passwordService", passwordService);
        Whitebox.setInternalState(testObject, "validator", validator);
        Whitebox.setInternalState(testObject, "reportService", reportService);
    }

    @Test
    public void testSomething() {
    }
}

如果您正在进行常规单元测试,Whitebox可以帮助您在没有弹簧环境的情况下进行测试。我强烈推荐这种方法(但它有点偏离主题,我不会发布我写的例子,之前我注意到你正在进行集成测试;))。


编辑:如果您使用的是Mockito的更高版本,您会注意到Whitebox已经消失,那么该怎么做呢? 我遇到了这种情况,并征求意见:What do I use instead of Whitebox in Mockito 2.2 to set fields?

答案 1 :(得分:1)

您不再需要这样做了。 Mockito本身从版本1.8.3开始,现在支持带注释的模拟和模拟注入,如下所述:http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/Mockito.html#21

我们现在进行以下单元测试:

// No annotation required
public class SomeTest {

  @Mock
  private SomeDependency someDependency;
  @Mock
  private SomeDependency2 someDependency2;
  @InjectMocks
  private ClassUnderTest classUnderTest;

  @BeforeMethod(alwaysRun = true)
  public void setUp() {
    MockitoAnnotations.initMocks(this);
  }

  public void testSomething() {
    // Do your Mockito test here.
  }
}