Spring #Autowired object is null - spring

I'm writing a specification class in spock framework.
#ContextConfiguration(classes = [MyServiceImplementation.class])
class MyServiceSpecification extends Specification {
#Autowired
private MyService myServiceImplementation
def " " {
//code
}
}
The class MyServiceImplementation is annotated #Service. I'm not using XML configuration. MyServiceImpl is an implementation of the interface: MyService.
Why is the autowired object myServiceImplementation null?
I tried using ComponentScan and it still didn't work.

First, you need to have both spock-core and spock-spring on the classpath. Second, #ContextConfiguration(classes= takes a list of configuration classes, not bean classes.
#ContextConfiguration(classes = [MyConfig.class])
class MyServiceSpecification extends Specification {
#Autowired
private MyService myServiceImplementation
def " " {
//code
}
}
// You could also define #ComponentScan here
class MyConfig {
#Bean
MyService myService() {
return new MyServiceImplementation();
}
}

Related

I can't autowire Service class in Spring Boot Test

I created Dao Repository that uses jdbc for working with DB.
I autowired this repository in my Service class.
Then I try to autowire my service class in my test class.
#SpringBootTest
public class ServiceTest {
#MockBean
private Dao dao;
#Autowired
private Service service;
#Test
void whenSomething_thanSomething() {
when(Dao.getStatus(anyString())).thenReturn("green");
assertEquals(0, service.getStatus(""));
}
//other tests...
}
#Service
public class Service {
private DaoImpl daoImpl;
#Autowired
public Service(DaoImpl daoImpl) {
this.daoImpl = daoImpl;
}
//...
}
#Repository
public class DaoImpl omplements Dao {
private NamedParameterJdbcOperations jdbc;
#Autowired
public DaoImpl(NamedParametedJdbcOperations jdbc) {
this.jdbc = jdbc;
}
//...
}
When I start test I get the next error:
Parameter 0 of constructor in Service required a bean of type DaoImpl that could not be found.
How can I resolve my problem?
Since you inject DaoImpl in your service-class you were probably intending to mock DaoImpl instead of Dao:
#SpringBootTest
public class ServiceTest {
#MockBean
private DaoImpl daoImpl;
...
}

How to use #TestConfiguration

How to override #Configuation which is present under src/main/java with #TestConfiguration during unit tests?
#Configuration
public class AppConfig {
#Bean
public EmployeeService employeeService(){
return new EmployeeService();
}
}
#Component
public class ServerStartSetup implements CommandLineRunner {
#Autowired
private EmployeeService employeeService;
public void run(String... args) {
// do something with employee service
}
}
I would like to override the above bean with some below custom bean for testing purposes.
#TestConfiguration
public class TestAppConfig {
#Bean
public EmployeeService employeeService(){
return new FakeEmployeeService();
}
}
#SpringBootTest
#Import(TestAppConfig.class)
public class UnitTest {
}
However AppConfig does not seem to be skipped. That is , it throws an error saying that there is a bean with same name employeeService. If I rename bean method name in the TestAppConfig, it injects the bean created via AppConfig.
How to fix this.?
Note: One possible solution is using #Profile. I am looking for anything other than using Profiles.
I tested locally and found that changing the method name or #Bean to #Bean("fakeEmployeeService") and adding the #Primary annotation works.
#SpringBootTest
class DemoApplicationTests {
#Autowired
private EmployeeService employeeService;
#TestConfiguration
static class TestConfig {
//#Bean("fakeEmployeeService")
#Bean
#Primary
public EmployeeService employeeServiceTest() {
return new EmployeeService() {
#Override
public void doSomething() {
System.out.println("Do something from test...");
}
};
}
}
...
}
If we want to override a bean definition in #TestConfiguration, we need:
To use the same name as the overridden bean. (Otherwise it would be an "additional" bean and we could get conflict/'d have to qualify/primary)
Since spring-boot:2.1: spring.main.allow-bean-definition-overriding=true (set this in tests ONLY!plz!)
#ref
Then, with:
#TestConfiguration
public class TestAppConfig {
#Bean // when same name, no #Primary needed
public EmployeeService employeeService(){ // same name as main bean!
return new FakeEmployeeService();
}
}
We can do that:
#Import(TestAppConfig.class)
#SpringBootTest(properties = "spring.main.allow-bean-definition-overriding=true")
public class UnitTest {
... // EmployeeService will be "fake", the rest is from "main config"
You can mock the AppConfig bean in your test like this:
#MockBean
private AppConfig config;
Or, like you said, just use profiles.

HK2 Jersey. Way create #Service with env options

As I see, I can use autosearch annotation #Service to create singleton to use that via #Inject. Like:
#Service
class MyService {
//.....
}
#Service
class MyOtherService {
#Inject MyService myService;
//.....
}
But would like to create Service using options that depends on environment.
I could do that using AbstractBinder like:
final ResourceConfig resourceConfig = new ResourceConfig()
.register(new AbstractBinder() {
#Override
protected void configure() {
String someOption = "optionOne";
String anotherOption = "optionTwo";
MyService myService = new MyService.create(someOption, anotherOption);
bind(MyService).to(MyService.class).in(Singleton.class);
}
})
But how can I do just the same but using annotation autoconfig style? Without creating AbstractBinder object.

Inner static class with #Configuration picked up by spring scanner for all tests. What is wrong?

Spring picks up inner #Configuration for Test1 from Test2. I need a mocked IService in Test2 but a real ServiceImpl in Test1. Also I want to have common TestConfiguration for all my tests. But I always have mocked IService in both tests. What is wrong?
How I can disable inner configurations picking up for sibling tests?
Here is my code :
ServiceImpl.java:
#Service
public class SeriviveImpl implements IService {
}
TestConfiguration.java:
#Configuration
#ComponentScan
public class TestConfiguration {
// empty
}
Test1.java:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestConfiguration.class})
public class Test1 {
#Autowired
private IService service;
}
Test2.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {Test2.CustomConfiguration.class, TestConfiguration.class})
public class Test2 {
#Autowired
private IService service;
#Configuration
static class CustomConfiguration {
#Bean
IService service() {
return mock(IService.class);
}
}
}
You can filter the inner class from the TestConfiguration #ComponentScan:
#Configuration
#ComponentScan(excludeFilters = {
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
value = Test2.CustomConfiguration.class)
})
public class TestConfiguration {
// empty
}
This will prevent it being picked up in Test1.
EDIT Or if you have lots of inner configuration you can create your own annotation and filter all these annotated classes from your #ComponentScan:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Configuration
public #interface InnerConfiguration {
}
Then use this annotation on your inner classes instead of #Configuration:
#InnerConfiguration
static class CustomConfiguration {
#Bean
IService service() {
return mock(IService.class);
}
}
and filter these out of your component scan like this:
#Configuration
#ComponentScan(excludeFilters = {
#ComponentScan.Filter(type = FilterType.ANNOTATION,
value = InnerConfiguration.class)
})
public class TestConfiguration {
// empty
}
Since you explicitly use Test2.CustomConfiguration.class in Test2's #ContextConfiguration annotation, you can remove #Configuration annotation from Test2.CustomConfiguration and it will not be picked up by #ComponentScan during Test1 run.
This works because:
To load an ApplicationContext for your tests by using annotated
classes (see Java-based container configuration), you can annotate
your test class with #ContextConfiguration and configure the classes
attribute with an array that contains references to annotated classes.
and
The term “annotated class” can refer to any of the
following:
A class annotated with #Configuration.
A component (that is, a class annotated with #Component, #Service,
#Repository, or other stereotype annotations).
A JSR-330 compliant class that is annotated with javax.inject
annotations.
Any other class that contains #Bean methods.
See https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext-ctx-management-javaconfig
You can probably achieve that by using org.springframework.beans.factory.annotation.Qualifier annotation to explicitly select the desired implementation. Here is how your code might look like then:
...
#Service
#Qualifier("impl")
public class SeriviveImpl implements IService {}
...
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestConfiguration.class})
public class Test1 {
#Autowired
#Qualifier("impl")
private IService service;
}
...
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {Test2.CustomConfiguration.class,TestConfiguration.class})
public class Test2 {
#Autowired
#Qualifer("mock")
private IService service;
#Configuration
static class CustomConfiguration {
#Bean
#Qualifier("mock")
IService service() { return mock(IService.class); }
}
}

Why spring #autowired is null?

I'm trying to autowire a service in my rest controller like these:
rest controller:
#ApplicationPath("/greetings")
#Component(immediate = true, service = Application.class)
public class RestControllerApplication extends Application {
#Autowired
private MyService myService;
public Set<Object> getSingletons() {
return Collections.<Object>singleton(this);
}
#POST
#Path("/getUploadType")
#Produces("application/json")
public JsonObject getUploadType() {
...
myService.findUploadTypes();
...
}
}
service:
#Component
public class UploadService {
private static final Logger log = Logger.getLogger(UploadService.class);
#Autowired
private OneDAO oneDAO;
#Autowired
private TwoDAO twoDAO;
...
}
but in my rest controller, uploade service is null. Why?
Spring uses its own set of annotations. Instead of #Path plus #[HTTP method] you should use #RequestMapping.
You can find an example here
There is also an extended example here
I have got access to my bean, with these few line of code:
WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
MyService myService = context.getBean(MyService.class);
You are declaring a UploadService as #Component but trying to autowire a MyService instance in your controller...
There are two options: you can declare correct service type in your controller or you can make UploadService inheriting from MyService.

Resources