Gostaria de injetar um objeto simulado do Mockito em um bean Spring (3+) para fins de teste de unidade com o JUnit. Atualmente, minhas dependências de bean são injetadas usando a @Autowired
anotação em campos de membros privados.
Eu considerei usar, ReflectionTestUtils.setField
mas a instância do bean que desejo injetar é na verdade um proxy e, portanto, não declara os campos de membros privados da classe de destino. Não desejo criar um setter público para a dependência, pois modificarei minha interface apenas para fins de teste.
Segui alguns conselhos da comunidade Spring, mas o mock não foi criado e a fiação automática falha:
<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.package.Dao" />
</bean>
O erro que encontro atualmente é o seguinte:
...
Caused by: org...NoSuchBeanDefinitionException:
No matching bean of type [com.package.Dao] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency.
Dependency annotations: {
@org...Autowired(required=true),
@org...Qualifier(value=dao)
}
at org...DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(D...y.java:901)
at org...DefaultListableBeanFactory.doResolveDependency(D...y.java:770)
Se eu definir o constructor-arg
valor como algo inválido, nenhum erro ocorrerá ao iniciar o contexto do aplicativo.
Respostas:
A melhor maneira é:
Atualização
No arquivo de contexto, esta simulação deve ser listada antes que qualquer campo com conexão automática, dependendo dele, seja declarado.
fonte
Isso injeta qualquer objeto zombado na classe de teste. Nesse caso, ele injetará mockedObject no testObject. Isso foi mencionado acima, mas aqui está o código.
fonte
mockedObject
?Mockito.spy(...)
issomockedObject
? E então usewhen(mockedObject.execute).thenReturn(objToReturn)
oudoReturn(objToReturn).when(mockedObject).execute()
. O segundo não invoca o método real. Você também pode verificar aMockito.doCallRealMethod()
documentaçãoEu tenho uma solução muito simples usando o Spring Java Config e o Mockito:
fonte
initMocks
? Por que não apenasreturn Mockito.mock(BeanA.class)
entrargetBeanA
? Dessa forma, é mais simples e há menos código. o que estou perdendo?Dado:
Você pode ter a classe que está sendo testada carregada via fiação automática, zombar da dependência do Mockito e usar o ReflectionTestUtils do Spring para injetar a zombaria na classe que está sendo testada.
Observe que, antes do Spring 4.3.1, esse método não funcionava com serviços atrás de um proxy (anotado com
@Transactional
, ouCacheable
, por exemplo). Isso foi corrigido pelo SPR-14050 .Para versões anteriores, uma solução é desembrulhar o proxy, conforme descrito aqui: A anotação transacional evita que os serviços sejam zombados (que é o que
ReflectionTestUtils.setField
faz por padrão agora)fonte
Se você estiver usando o Spring Boot 1.4, há uma maneira incrível de fazer isso. Basta usar uma nova marca
@SpringBootTest
em sua classe e@MockBean
em campo e o Spring Boot criará uma simulação desse tipo e a injetará no contexto (em vez de injetar a original):Por outro lado, se você não estiver usando o Spring Boot ou uma versão anterior, precisará trabalhar um pouco mais:
Crie um
@Configuration
bean que injete suas zombarias no contexto do Spring:Usando a
@Primary
anotação, você está dizendo ao Spring que esse bean tem prioridade se nenhum qualificador for especificado.Anote a classe
@Profile("useMocks")
para controlar quais classes usarão o mock e quais usarão o bean real.Por fim, em seu teste, ative o
userMocks
perfil:Se você não quiser usar o mock, mas o bean real, não ative o
useMocks
perfil:fonte
web.xml
e AnnotationConfigWebApplicationContext. Teve que usar em@WebAppConfiguration
vez de@WebIntegrationTest
e@ContextHierarchy
com em@ContextConfiguration
vez de@SpringApplicationConfiguration
.@Primary
anotação para o meu caso, pois havia uma chamada com falha dentro de uma@PostConstruct
que eu queria zombar, mas o@PostConstruct
bean do foi criado antes da zombaria para não usar a zombaria (até que eu adicionasse@Primary
).Desde o Mockito 1.8.3
@InjectMocks
- isso é incrivelmente útil. Meus testes JUnit são@RunWith
osMockitoJUnitRunner
e eu construo@Mock
objetos que satisfazem todas as dependências da classe que está sendo testada, que são todas injetadas quando o membro privado é anotado@InjectMocks
.I
@RunWith
aSpringJUnit4Runner
única agora para testes de integração.Observarei que não parece ser capaz de injetar
List<T>
da mesma maneira que o Spring. Ele procura apenas um objeto Mock que satisfaça oList
e não injeta uma lista de objetos Mock. A solução alternativa para mim foi usar uma@Spy
lista instanciada manualmente e adicionar manualmente os objetos simulados a essa lista para teste de unidade. Talvez isso tenha sido intencional, porque certamente me forçou a prestar muita atenção ao que estava sendo ridicularizado juntos.fonte
Atualização: Agora existem soluções melhores e mais limpas para esse problema. Por favor, considere as outras respostas primeiro.
Acabei encontrando uma resposta para isso de Ronen em seu blog. O problema que eu estava tendo é devido ao método que
Mockito.mock(Class c)
declara um tipo de retornoObject
. Consequentemente, o Spring não pode inferir o tipo de bean do tipo de retorno do método de fábrica.A solução de Ronen é criar uma
FactoryBean
implementação que retorne simulações. AFactoryBean
interface permite que o Spring consulte o tipo de objetos criados pelo bean de fábrica.Minha definição de bean zombado agora se parece com:
fonte
A partir do Spring 3.2, isso não é mais um problema. O Spring agora suporta a ligação automática dos resultados de métodos genéricos de fábrica. Consulte a seção "Métodos genéricos de fábrica" nesta postagem do blog: http://spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new-testing-features/ .
O ponto principal é:
O que significa que isso deve funcionar imediatamente:
fonte
O código abaixo funciona com fiação automática - não é a versão mais curta, mas é útil quando deve funcionar apenas com os jarros de mola / mockito padrão.
fonte
Se você estiver usando spring> = 3.0 , tente usar a
@Configuration
anotação Springs para definir parte do contexto do aplicativoSe você não quiser usar o @ImportResource, isso também pode ser feito ao contrário:
Para obter mais informações, consulte spring-framework-reference: Configuração de contêiner baseado em Java
fonte
Talvez não seja a solução perfeita, mas costumo não usar a mola para fazer DI para testes de unidade. as dependências de um único bean (a classe em teste) geralmente não são muito complexas, então eu apenas injeto diretamente no código de teste.
fonte
Eu posso fazer o seguinte usando o Mockito:
fonte
Publicando alguns exemplos com base nas abordagens acima
Com primavera:
Sem mola:
fonte
Atualização - nova resposta aqui: https://stackoverflow.com/a/19454282/411229 . Esta resposta se aplica apenas às versões Spring anteriores à 3.2.
Procurei por um tempo uma solução mais definitiva para isso. Esta postagem do blog parece cobrir todas as minhas necessidades e não depende da ordem das declarações do bean. Todo o crédito a Mattias Severson. http://www.jayway.com/2011/11/30/spring-integration-tests-part-i-creating-mock-objects/
Basicamente, implemente um FactoryBean
Atualize sua configuração de primavera com o seguinte:
fonte
Observando o ritmo de desenvolvimento de Springockito e o número de questões em aberto , eu ficaria um pouco preocupado em introduzi-lo na minha pilha de suítes de teste atualmente. O fato de o último lançamento ter sido feito antes do lançamento do Spring 4 traz questões como "É possível integrá-lo facilmente ao Spring 4?". Não sei, porque não tentei. Prefiro a abordagem pura do Spring se eu precisar zombar do Spring bean no teste de integração.
Existe uma opção para falsificar o Spring bean com recursos simples do Spring. Você precisa usar
@Primary
,@Profile
e@ActiveProfiles
anotações para ele. Eu escrevi uma postagem no blog sobre o assunto.fonte
Encontrei uma resposta semelhante ao teabot para criar um MockFactory que fornece os zombadores. Usei o exemplo a seguir para criar a fábrica de simulação (já que o link para o narkisr está morto): http://hg.randompage.org/java/src/407e78aa08a0/projects/bookmarking/backend/spring/src/test/java/ org / randompage / bookmarking / backend / testUtils / MocksFactory.java
Isso também ajuda a impedir que o Spring queira resolver as injeções do feijão zombado.
fonte
isto ^ funciona perfeitamente se declarado primeiro / no início do arquivo XML. Mockito 1.9.0 / Primavera 3.0.5
fonte
Uso uma combinação da abordagem usada na resposta por Markus T e uma implementação auxiliar simples
ImportBeanDefinitionRegistrar
que procura uma anotação personalizada (@MockedBeans
) na qual é possível especificar quais classes devem ser ridicularizadas. Acredito que essa abordagem resulta em um teste de unidade conciso com parte do código padrão relacionado à zombaria removida.Veja como um teste de unidade de amostra se parece com essa abordagem:
Para fazer isso acontecer, você precisa definir duas classes auxiliares simples - anotação personalizada (
@MockedBeans
) e umaImportBeanDefinitionRegistrar
implementação customizada .@MockedBeans
a definição de anotação precisa ser anotada@Import(CustomImportBeanDefinitionRegistrar.class)
eImportBeanDefinitionRgistrar
precisa incluir definições de beans simulados na configuração em seuregisterBeanDefinitions
método.Se você gosta da abordagem, pode encontrar exemplos de implementações no meu blog .
fonte
Eu desenvolvi uma solução baseada na proposta de Kresimir Nesek. Adicionei uma nova anotação @EnableMockedBean para tornar o código um pouco mais limpo e modular.
Eu escrevi um post explicando isso.
fonte
Eu sugeriria migrar seu projeto para o Spring Boot 1.4. Depois disso, você pode usar uma nova anotação
@MockBean
para falsificar suacom.package.Dao
fonte
Hoje eu descobri que um contexto de primavera em que eu declara um antes dos grãos Mockito estava falhando ao carregar. Depois de mover APÓS as zombarias, o contexto do aplicativo foi carregado com sucesso. Cuidar :)
fonte
Para o registro, todos os meus testes funcionam corretamente, apenas tornando o equipamento inicializado preguiçosamente, por exemplo:
Suponho que o raciocínio é o que Mattias explica aqui (na parte inferior do post), que uma solução alternativa está mudando a ordem em que os beans são declarados - a inicialização lenta é "uma espécie de" ter o equipamento declarado no final.
fonte
Se você usa Injeção de Controlador, verifique se suas variáveis locais NÃO são "finais"
fonte