Question

J'ai un problème pour essayer de se moquer d'une propriété d'un service à partir d'un test 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);
    }
}

Mocking Foodao n'a aucun effet puisque le résultat n'est pas attendu. Voici le code du service et du 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";
    }
}

Comme nous pouvons le voir, le service réel est destiné à retourner "foo", mais le test se moque du DAO pour que le service renvoie "var". Je sais que c'est une chose liée au proxy CGLIB, mais je ne peux pas comprendre comment le faire fonctionner sans utiliser de setter pour la propriété Foodao. Toute aide serait appréciée.

Cordialement et merci d'avance.

Était-ce utile?

La solution

Réponse courte

Vous devez déballer le proxy et définissez le champ sur l'objet cible:

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

La unwrapFooService() peut être défini comme suit:

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

...une longue

Le problème est assez complexe, mais résoluble. Comme vous l'avez deviné, il s'agit d'un effet secondaire des proxys CGLIB utilisés. En principe, le printemps crée une sous-classe de votre FooServiceImpl nommé similaire à FooServiceImpl$EnhancerByCGLIB. Cette sous-classe contient un référence à l'original FooServiceImpl ainsi que ... tous les champs FooServiceImpl a (ce qui est compréhensible - il s'agit d'une sous-classe).

Il y a donc deux variables: FooServiceImpl$EnhancerByCGLIB.fooDao et FooServiceImpl.fooDao. Vous attribuez une simulation au premier mais votre service utilise ce dernier ... je a écrit À propos de ces pièges il y a quelque temps.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top