I am having some issues with testing my camel context with spring boot.
I am using spring boot 1.5.6, spock 1.1-groovy-2.4, camel 2.19.2, and camel-spring-boot-starter 2.19.2.
I am using a spock mock, and I'm using the DetachedMockFactory in a #TestConfiguration class. All of my beans use constructor injection. I am injecting a mocked #Repository into one of the processor #Components, and I am also injecting it into my test class to define interactions.
I have my test annotated with #SpringBootTest with the classes list including all Processor implementations, and all RouteBuilder extensions. I also have an '#Import' with my TestConfiguration class. I am even using constructor injection for this repository bean in my test!
But it seems that the mock that is injected into the test class is not the one that is in use. Does anyone have an idea what could be wrong? I have tried #DirtiesContext to reload the context both before and after each test, but that did not help.
Problems with DetachedMocks not behaving correctly, e.g., appearing to be the same instance, are usually caused by some framework wrapping them in proxies. For example this can be caused by #Transactional annotation in Spring, which creates a proxy to facilitate jdbc-session management. See also issue #758
For spring you can use the methods of AopUtils (jdoc). The simple way is to use AopUtils.isAopProxy to check if it is proxied by spring an then unwrap it.
public static <T> T getTargetObject(Object proxy) throws Exception {
if (AopUtils.isAopProxy(proxy)) {
return (T) ((Advised) proxy).getTargetSource().getTarget();
} else {
return (T) proxy;
}
}
And in a Test
def "sample service test"() {
given:
def sampleRepositryMock = getTargetObject(sampleRepositry)
when:
sampleService.doSomething() // simply invoke sampleRepositry.doSomething() in it
then:
1 * sampleRepositryMock.doSomething()
0 * _
}
Edit: Since Spock 1.2 there is an extension to automatically unwrap injected beans #UnwrapAopProxy.
#Inject
#UnwrapAopProxy
SampleRepositry sampleRepositryMock
If someone comes up with the same problem.
Spock added additional #UnwrapAopProxy that will do the job for you instead of the util method mentioned above. You can also drop the DetachedMockFactory
#SpringSpy
#UnwrapAopProxy
Service service
Related
I have a service, a bean, that contains a #Transactional method:
public class InMessageService {
...
#Transactional
public boolean retryInMessage(String messageId) {
...
}
}
For testing, I try to mock that service with Mockito:
#Bean
#Primary
public InMessageService inMessageService() {
return Mockito.mock(InMessageService.class);
}
The result of this is the following exception when I start the test:
Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB
subclass of class somePackage.InMessageService$MockitoMock$156222813: Common
causes of this problem include using a final class or a non-visible class;nested exception is
org.springframework.cglib.core.CodeGenerationException: java.lang.NoClassDefFoundError-->
somePath/InMessageService$MockitoMock$156222813
I want to mention that the same code was working with spring-boot 1.2.1 and Mockito 1.10.19. I try to run the above code with spring boot 2.1.1 and Mockito 2.23.0
My observations so far:
No matter what Mockito version between 2.1.0 and 2.23.0 I use, the exception is the same. I cannot (and don't want to) use older versions of Mockito as the project does not compile any more
If I temporary remove the #Transactional annotation, the exception is not thrown.
Any ideas what has to be adjusted with the upgrade of spring boot so the tests work again ?
Thank you !
Since version 2.1.0 Mockito retains annotations on proxied methods. This means that Spring attempts to proxy the mock class that declares a transactional annotation and this fails, because the mocking method is final.
Before, Mockito stripped these annotations what would have caused any real method call to fail due to the missing transaction.
To avoid this, you would need to strip the annotations of the mock. You can do so using MockSettings.withoutAnnotations.
This should happen quite often:
RestController -> SomeClass -> SomeOtherClass -> YetAnotherClass and so on...
In my specific case there is a chain of responsibility which is injected to a rest controller. Each class is injected to it's previous class in the above chain.
I have implemented this with spring boot and I'm trying to test the REST resource. I want to Mock the "YetAnotherClass" so that when I send a request with MockMvc I can verify that something has happened in the mock object.
The problem is if I use #MockBean to mock YetAnotherClass then I have to inject it to SomeOtherClass. I have tried to inject it with #TestConfiguration but it seems that the Mock object injection doesn't work this way when the request is sent through MockMvc and the mock object is nested deep inside a chain such as above. (The original bean is injected not the mock one)
I know that JMockit mocks every instance of a class so it would solve my problem. But Spring boot defaults to Mockito and I prefer to avoid inconsistencies.
How can I implement such a test scenario?
I've run into a lot of annoyance using Mockito's annotation config setup when setting up Spring JUnit text fixtures.
I've found the way I like mocking beans with external integrations like this this by essentially having a separate MockObjectsConfig class with the mock objects I want using the standard Spring Context Configuration, and then import it alongside my real test config:
#Configuration
public class MockObjectsConfig {
#Bean
public YetAnotherClass yetAnotherClass() {
Mockito.mock(YetAnotherClass.class); // and add any thenReturns, answers, etc. here
}
... More mock beans...
}
Then include it in your test like so:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = { MyRealConfigClass.class, MockObjectsConfig.class)
public class MyJunitTest {
#Autowired
private RestController restController;
}
You can also annotate your mock bean with #Profile and test with #ActiveProfiles if you need to prevent a conflict there.
This way your mock YetAnotherClass will get injected into your context like all your other beans -- no relying on, mixing, and fiddling around with Mockito and other library annotations.
My test class looks like this
#SpringBootTest(webEnvironment=WebEnvironment.MOCK)
public class sampleClassTest{
#Autowired
private WebApplicationContext wac;
}
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
In the setup method, wac is always null. From spring boot documentation, #SpringBootTest(webEnvironment=WebEnvironment.MOCK) always created a mock WebapplicaitonContext.
So I would expect it get autowired in the code above which doesn't happen.
Can someone tell me how to go about creating a webapplicationContext in this case so that it's not null like in my case ?
UPDATE
I am running spring boot tests invoking them from a class with springboot annotation.
Both test (springboottest) and calling class (springboot) application are in the same spring boot project under src/main/java.
I have nothing under src/main/test. I have done in this way because if classes from src/main/java want to call a test class then, it isn't really a test class.
Now, the problem is that I can't use runWith(SpringRunner.class) in springbootTest class. If I did that to get a mock webApplicationContext then, it gives me this error:
javax.management.InstanceAlreadyExistsException: org.springframework.boot:type=Admin,name=SpringApplication
I am not sure how to do about this.
To use #SpringBootTest you need to use Spring Framework's test runner. Annotate your test class with #RunWith(SpringRunner.class).
If someone is struggling with this issue in 2022 - please keep my defined precondions in mind. If you are using #SpringBootTest with defined port and constructor auto-wiring of the test class, the application context might be null.
It seems that the constructor dependency injection is eager and the cache aware context delegate of Spring is searching for a web application context which is no available yet. If you use field auto-wiring your test might run in a deterministic manner.
Whoever is facing this issue, make sure your spring boot starter parent version is compatible with spring cloud version in pom.xml
I was facing same issue, i resolved it by doing same.
I have a spring managed application in which I like for my service layer to be mocked. So I created a Spring Application Java Config and returned a mock of the actual service.
For e.g,
#Bean
#Profile("resource")
public MyService mockService() {
return mock(MyService.class)
}
And then the MyService goes as
class MyService {
#Autowired
private MyDao dao;
}
When Spring creates bean of name "mockService", it also tries to Autowire MyDao on the mock ? This in my opinion defeats the purpose of mocking. Is this the expected behaviour, whats the workaround ?
So bottomline, its best practice to code to interfaces rather than concrete classes particularly if you are wanting to write focused tests on specific layers.
Well, I suppose you're trying to execute an integration test, but using some mocks, doing something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {Application.class, YourMockConfigurationClass.class})
#WebIntegrationTest
public class MyIntegrationTest {
...
}
In this case, you can create a mock of your service, and tell spring that this service instance has priority:
#Bean
#Primary
public MyService mockService() {
return mock(MyService.class)
}
Doing this, whenever Spring has to inject an unqualified MyService instance, it always chooses your mock. But it doesn't prevent Spring to create the original service.
So inside Spring context there will be two instances of MyService, the mock instance and the original implementation. But using #Primary you're telling Spring that your mock has priority.
On the other hand, in order to prevent Spring to load any mock in your environments (dev, test, etc...), you should annotate your mock configuration class either with a testing #Profile or a custom annotation (like #MockBean) and then configure your component scan strategy to not load this class (#ComponentScan(excludeFilters=#Filter(MockBean.class)))
I would like to know if it's possible to use Spring to resolve the dependencies of an object created manually in my program. Take a look at the following class:
public class TestClass {
private MyDependency md;
public TestClass() {
}
...
public void methodThaUsesMyDependency() {
...
md.someMethod();
...
}
}
This TestClass is not a spring bean, but needs MyDependency, that is a spring bean. Is there some way I can inject this dependency through Spring, even if I instantiate TestClass with a new operator inside my code?
Thanks
Edit: The method I'm describing in my original answer below is the general way to accomplish DI external of the container. For your specific need - testing - I agree with DJ's answer. It's much more appropriate to use Spring's test support, for example:
#Test
#ContextConfiguration(locations = { "classpath*:**/applicationContext.xml" })
public class MyTest extends AbstractTestNGSpringContextTests {
#Resource
private MyDependency md;
#Test
public void myTest() {
...
While the above example is a TestNG test, there is also Junit support explained in 8.3.7.2. Context management and caching.
General approach: Annotate your class with #Configurable and utilize AspectJ load-time or compile-time weaving. See 6.8.1 in the Spring documentation on AOP for more details.
You can then annotate your instance variables with #Resource or #Autowired. Though they accomplish the same goal of dependency injection, I recommend using #Resource since it's a Java standard rather than Spring-specific.
Lastly, remember to consider using the transient keyword (or #Transient for JPA) if you plan on serializing or persisting the objects in the future. Chances are you don't want to serialize references to your DI'd repository, service, or component beans.
See the autowire() method on the AutowireCapableBeanFactory class. If you use an ClasspathXmlApplicationContext, you can get the factory with getAutowireCapableBeanFactory()
To get the ApplicationContext, you would need to use a static singleton or other central repository, such as JNDI or a Servlet container. See DefaultLocatorFactory on how to get an instance of the ApplicationContext.
If what you need is for testing purposes, Spring has good support for the scenario that you described above.
Check out Spring Reference manual section on Testing