17

Junit テスト内からサービスのプロパティをモックしようとすると、問題が発生します。

@ContextConfiguration("classpath:application-config.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class FooServiceTests {

    @Autowired
    private FooServiceImpl fooService;

    @Test
    public void testFoo() {
        String str = fooService.foo();
        assertEquals("Var", str);
    }

    @Before
    public void mockFooDao() throws Exception {
        FooDao mockFooDao = Mockito.mock(FooDao.class);
        Mockito.when(mockFooDao.foo()).thenReturn("Var");
        ReflectionTestUtils.setField(fooService, "fooDao", mockFooDao);
    }
}

結果が期待どおりではないため、fooDao をモックしても効果はありません。サービスと dao の両方のコードは次のとおりです。

@Service("fooService")
public class FooServiceImpl implements FooService {

    @Autowired
    protected FooDao fooDao;

    @Override
    public String foo() {
        return fooDao.foo();
    }
}

@Repository
public class FooDaoImpl implements FooDao {

    @Override
    public String foo() {
        return "foo";
    }
}

ご覧のとおり、実際のサービスは「foo」を返すようになっていますが、テストは dao をモックするため、サービスは「var」を返します。私はそれがCGLIBプロキシ関連のものであることを知っていますが、fooDaoプロパティのセッターを使用せずに機能させる方法を理解できません。どんな助けでも大歓迎です。

よろしくお願いします。

4

1 に答える 1

40

簡潔な答え

プロキシをアンラップし、ターゲット オブジェクトにフィールドを設定する必要があります。

ReflectionTestUtils.setField(unwrapFooService(), "fooDao", mockFooDao);

は次のunwrapFooService()ように定義できます。

private FooServiceImpl unwrapFooService() {
  if(AopUtils.isAopProxy(fooService) && fooService instanceof Advised) {
      Object target = ((Advised) fooService).getTargetSource().getTarget();
      return (FooServiceImpl)target;
  }
  return null;
}

...長いもの

問題は非常に複雑ですが、解決可能です。ご想像のとおり、これは使用されている CGLIB プロキシの副作用です。原則として、Spring は にFooServiceImpl似た名前付きのサブクラスを作成しますFooServiceImpl$EnhancerByCGLIB。このサブクラスには、元の参照FooServiceImplだけでなく...すべてのフィールドFooServiceImplが持っている参照が含まれています(これは理解できます-これはサブクラスです)。

つまり、実際には と の 2 つの変数がFooServiceImpl$EnhancerByCGLIB.fooDaoありFooServiceImpl.fooDaoます。あなたは前者にモックを割り当てていますが、あなたのサービスは後者を使用しています...私はこの落とし穴について少し前に書きました。

于 2012-01-27T13:35:27.673 に答える