Spring Junit and annotation based autowiring - spring

I added a junit test to a simple spring example but it fails to autowire the json service that I wrote.
What is needed to get autowiring to work in a spring JUnit tests?
To try the failing project out do ...
git clone https://bitbucket.org/oakstair/spring-boot-cucumber-example
cd spring-boot-cucumber-example
./gradlew test
Thanks in advance!
Application
#SpringBootApplication
#ComponentScan("demo")
public class DemoApplication extends SpringBootServletInitializer {
Service interface
#Service
public interface JsonUtils {
<T> T fromJson(String json, Class<T> clazz);
String toJson(Object object);
}
Service implementation
#Component
public class JsonUtilsJacksonImpl implements JsonUtils {
Test
#ContextConfiguration()
#RunWith(SpringJUnit4ClassRunner.class)
#ComponentScan("demo")
public class JsonUtilsTest {
#Autowired
private JsonUtils jsn;

In your JsonUtilsTest you can't put a #ComponentScan on the class level here since it isn't a #Configuration class. With a #ContextConfiguration annotation like you are using here it is first looking for a static inner #Configuration class so add one of those with the #ComponentScan and it should work:
#ContextConfiguration()
#RunWith(SpringJUnit4ClassRunner.class)
public class JsonUtilsTest {
#Autowired
private JsonUtils jsn;
#Test
// Note: This test is not tested since I haven't got autowiring to work.
public void fromJson() throws Exception {
Integer i = jsn.fromJson("12", Integer.class);
assertEquals(12, (int) i);
}
#Test
// Note: This test is not tested since I haven't got autowiring to work.
public void toJson() throws Exception {
assertEquals("12", jsn.toJson(new Integer(12)));
}
#Configuration
#ComponentScan("demo")
public static class TestConfiguration {
}
}
EDIT: Or you can make Spring boot do the work for you by using the #SpringBootTest annotation with a SpringRunner instead:
#RunWith(SpringRunner.class)
#SpringBootTest
public class JsonUtilsTest {

Adding this to the test class fixed my problems!
#ContextConfiguration(classes = {DemoApplication.class})

Add #SpringBootTest
On your test class
And provide your SpringBootApplication class and Json utils class to the classes field of #SpringBootTest
It should look like this
#ContextConfiguration()
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes={<package>.DemoApplication.class, <package>.JsonUtil.class } )
#ComponentScan("demo")
public class JsonUtilsTest {

Related

How to #Autowired a test controller invoked by WebTestClient in #WebFluxTest?

I have a Controller just for tests with dummy apis
#RestController
public class TestController {
#Autowired
private org.springframework.cloud.sleuth.Tracer tracer;
#GetMapping("/trace")
public Mono<String> traceTest() {
...
}
}
Here's my test
#WebFluxTest(controllers = TestController.class)
public MyTest {
#Autowired
private WebTestClient webClient;
#Test
public void testTrace() {
webClient.get().uri("/trace")...
}
}
When I try to run this, my Tracer is not Autowired.
Of course, if I change my test to a #SpringBootTest, it all works
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public MyTest {
...
}
But I'd like to avoid autowiring my entire spring application. How can I get TestController to be auto-configured?
The #WebFluxTest annotation populates a Spring TestContext with only a subset of the relevant beans:
#WebFluxTest auto-configures the Spring WebFlux infrastructure and
limits scanned beans to #Controller, #ControllerAdvice,
#JsonComponent, Converter, GenericConverter, WebFilter, and
WebFluxConfigurer. Regular #Component and #ConfigurationProperties
beans are not scanned when the #WebFluxTest annotation is used.
#EnableConfigurationProperties can be used to include
#ConfigurationProperties beans. from Spring Boot Documentation
Your Tracer won't be part of this TestContext out-of-the-box.
For your #WebFluxTest you can provide a mocked version of this bean with #MockBean:
#WebFluxTest(controllers = TestController.class)
public MyTest {
#Autowired
private WebTestClient webClient;
#MockBean
private Tracer tracer;
#Test
public void testTrace() {
webClient.get().uri("/trace")...
}
}
... and if you want to test the full integration I would rather use #SpringBootTest.

Is there a way to include a spring component in a WebMvcTest

Given production code classes:
#RestController
#RequiredArgsConstructor
public class MyController {
private final MyValidator validator;
// annotations relating to request mapping excluded for brevity
public void test(#Valid #RequestBody final MyParams params) {
// do stuff
}
#InitBinder
#SuppressWarnings("unused")
protected void initBinder(final WebDataBinder binder) {
binder.setValidator(validator);
}
}
and
#Component
#RequiredArgsConstructor
public class MyValidator implements Validator {
...
#Override
public void validate(final Object target, final Errors errors) {
// custom validation
}
}
and finally test code:
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest {
// tests
}
I encounter the error:
NoSuchBeanDefinitionException: No qualifying bean of type 'MyValidator' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
I think the error is fair enough. I've annotated the test as a WebMvcTest, which I believe has excluded #Component beans. This is intentional and desired (from the perspective that I am only wanting to test the "web layer", not the whole context - it just so happens I need a component which is related/used only in the controllers)
My question, therefore, is: how can one explicitly include a component like a validator in the test context for a web test?
My environment is java version "10.0.2" 2018-07-17, spring boot 1.5.16.RELEASE.
There are two ways to solve this.
Using #SpringBootTest and #AutoConfigureMvc instead of #RunWith(SpringRunner.class) and #WebMvcTest.
#SpringBootTest
#AutoConfigureMvc
public class MyControllerTest {
}
Creating a #TestConfiguration class that injects the 'MyValidator' bean as:
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest {
#TestConfiguration
static class TestConfig {
#Bean
MyValidator getMyValidator(){
return new MyValidator();
}
}
// tests
}
More on this can be found here : https://mkyong.com/spring-boot/spring-boot-how-to-init-a-bean-for-testing/
There are two ways to test the web layer
first.
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyControllerTest {
#Autowired
private MyController myController;
}
The #SpringBootTest annotation tells Spring Boot to go and look for a
main configuration class (one with #SpringBootApplication for
instance), and use that to start a Spring application context.
A nice feature of the Spring Test support is that the application
context is cached in between tests, so if you have multiple methods in
a test case, or multiple test cases with the same configuration, they
only incur the cost of starting the application once. You can control
the cache using the #DirtiesContext annotation.
Secondly, if you want to use the #WebMvcTest(MyController.class)
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest {
#MockBean
private MyValidator validator;
}
But this validator is a fake, so you have to customize it for testing.
See this link for more details https://spring.io/guides/gs/testing-web/
I cannot recommend it as a standard practice but if you do need an instance of a dependency in your Web MVC tests (for example in legacy code) you can add them into the spring context using #SpyBean annotation.
Real methods of that class will be called during the test and you can verify them if needed similarly to the beans annotated with #MockBean
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest {
#SpyBean
private MyValidator validator
}

Spring Test Utils Autowired

I'm refactoring spring boot tests creating encapsulated classes with common behavior that will be injected in other tests. This classes has scope only in test package.
Spring ignore slices that will not be used in test (which is great and by design of spring boot test 1.5), but also ignore any #Component in src/test/java.
Question is how to configure spring boot test to pickup components in test/java?
I have one incomplete solution, that works for one test
My current solution is:
import com.example.testClasses.TestUtil;
#RunWith(SpringRunner.class)
#SpringBootTest
#Import(TestConfiguration.class)
public class ExampleTest {
#SpyBean
private ServiceDependency1 service1;
#Autowired
private TestUtil testUtil;
}
#Configuration
#ComponentScan(basePackages = "com.example.testClasses")
public class TestConfiguration {
}
#Component
public class TestUtil {
public TestUtil(ServiceDependency1 service) {
}
}
The solution above partial works, when another Utils is added TestUtils2 with different injection dependencies, this dependencies are not resolved.
That is because dependency for TestUtil1 is only solved with the #SpyBean, this is not the case in second test.
I put the all SpyBean on the TestConfiguration and use #AutoWired for each test.
import com.example.testClasses.TestUtil;
#RunWith(SpringRunner.class)
#SpringBootTest
#Import(TestConfiguration.class)
public class ExampleTest {
#Autowired
private ServiceDependency1 service1;
#Autowired
private TestUtil testUtil;
}
#Configuration
#ComponentScan(basePackages = "com.example.testClasses")
public class TestConfiguration {
#SpyBean
private ServiceDependency1 service1
#SpyBean
private ServiceDependency2 service2
}
#Component
public class TestUtil {
public TestUtil(ServiceDependency1 service) {
}
}

Use #Primary to precede an #Component/#Service annotated class

In my project I have a service with the #Service annotation.
#Service
public class ExampleService { ... }
I would like to override this service with a Mockito-mock, using a configuration file for my test in the following way:
public class TestContext{
#Bean
#Primary
public ExampleService exampleService(){
return mock(ExampleService.class);
}
}
and in my test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { WebContext.class, TestContext.class})
#WebAppConfiguration
public class TestExample{...}
For some reason the mocks aren't injected. The only way I can make this work is when I don't use #Service (#Component gives the same problem) but use a bean annotated method in the WebContext to load the ExampleService, and I put the TestClass behind the WebContext.class in the ContextConfiguration annotation (like in the code I wrote here). I don't get why, and I would like to know how I can keep using the #Service annotation.

DRY Spring AnnotationConfig testing

So, I'm working on some Spring tests which require dependency injection using annotations:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class BeanTest {
#Autowired
private SomeService someService;
#Configuration
static class ContextConfiguration {
#Bean
public SomeService someService() {
return new SomeService();
}
}
}
I'd really like to not have to repeat this code in every test but my attempts to create a base class which contains the configuration:
#Configuration
class MyContextConfiguration {
#Bean
public SomeService someService() {
return new SomeService();
}
}
And deriving from it:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class BeanTest {
#Autowired
private SomeService someService;
#Configuration
static class ContextConfiguration extends MyContextConfiguration {}
}
Don't seem to work. Can anybody suggest a way to DRY this up?
Thanks!
You should be able to do this instead.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class BeanTest {
#Autowired
private SomeService someService;
#Configuration
#Import(MyContextConfiguration.class)
static class ContextConfiguration {
....
}
}
Also, you don't need to mention AnnotationConfigContextLoader, Spring by convention will automatically pick up the static inner class annotated with #Configuration and use the appropriate ContextLoader
You can declare configuration classes in the the contextconfiguration-annotation. From the documentation.
ContextConfiguration
Defines class-level metadata that is used to determine how to load and configure an ApplicationContext for integration tests. Specifically, #ContextConfiguration declares the application context resource locations or the annotated classes that will be used to load the context.
Resource locations are typically XML configuration files located in the classpath; whereas, annotated classes are typically #Configuration classes. However, resource locations can also refer to files in the file system, and annotated classes can be component classes, etc.
example from the documentation.
#ContextConfiguration(classes = TestConfig.class)
public class ConfigClassApplicationContextTests {
// class body...
}

Resources