Unit Test with Spring JPA - #Autowired is not working - spring

I have a unit test and a helper class.
Unfortunely the Helper class' autowire does not work.
It works fine in MyTest class.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath*:context.xml"})
#Component
public class MyTest {
#Autowired
private Something something1;
#Autowired
private Something something2;
..
#Test
public void test1()
{
// something1 and something2 are fine
new Helper().initDB();
..
}
}
// Same package
public class Helper {
#Autowired
private Something something1;
#Autowired
private Something something2;
..
public void initDB()
{
// something1 and something2 are null. I have tried various annotations.
}
}
I'd like to avoid using setters because I have like 10 of those objects and different tests have different ones.
So what is required to get #Autowired working in Helper class? Thx!

You must not create the Helper class by a new statement, but you have to let spring create it to become a spring been and therefore its #Autowired fields get injected.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath*:context.xml"})
#Component
public class MyTest {
#Autowired
private Something something1;
#Autowired
private Something something2;
..
#Autowired
private Helper helper
#Test
public void test1() {
helper.initDB();
}
}
//this class must been found by springs component scann
#Service
public class Helper {
#Autowired
private Something something1;
#Autowired
private Something something2;
public void initDB(){...}
}

Your Helper class is not instanciated by spring ... You have to add an annotation like #component (if you are using package scan), or you can define the class as Bean in your springconfiguration class. But if you create the instance by yourself, it doesn't work

Related

#ConfigurationProperties, #Value not Working YET Passing the Tests

I have a strange problem reading configuration, none of solutions I've seen seem to work. Here is my code:
#SpringBootApplication
#EnableConfigurationProperties
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Here is my properties class
#Component
#ConfigurationProperties(prefix = "my")
#Data
#ToString
#NoArgsConstructor
#AllArgsConstructor
public class MyProperties {
private String host;
private int port;
}
I then use MyProperties class in my class using #Autowired:
#Autowired
private MyProperties props;
However, I'm getting null for my props object.
Strangely, this is passing the tests just perfectly:
#SpringBootTest
class ApplicationTests {
#Autowired
private MyProperties props;
#Test
void test_configuration() {
Assertions.assertEquals(props.getHost(), "xx.xx.xx.xx");//pass!
Assertions.assertEquals(props.getPort(), xxxxxx);//pass!
}
}
It has totally refused to work, and so has #Value injection. What could I be missing?
EDIT
Here's complete code of how I'm using #Autowired on MyProperties (I've included #Value which is also not working)
#Slf4j
#Component //also tried #Configurable, #Service
public class MyService {
#Autowired
private MyProperties props;
#Value("localhost")
public String host;
public void post() {
log.info(host + props);// =null and null
}
}
EDIT2
However, I've noticed that on the controller, it works perfectly okay:
#Slf4j
#RestController
#Service
public class Main {
#Autowired
private MyProperties props;
#Value("localhost")
private String host;
#GetMapping("/post")
public void post() {
log.info(host + props);//=it's perfect!
new MyService().post();// calling MyService - where #Autowired or #Value is failing
}
}
The reason this isn't working is because the MyService you're using isn't a Spring bean, but an instance you created by yourself (using new MyService()).
To make this work, you should autowire MyService, in stead of creating your own instance:
#Slf4j
#RestController
public class Main {
#Autowired // Autowire MyService
private MyService myService;
#GetMapping("/post")
public void post() {
myService.post(); // Use the myService field
}
}
For more information, look at this Q&A: Why is my Spring #Autowired field null.
UPDATE:
new MyService() is not a "spring bean", thus can't be auto-wired with anything!;)
1. Lombok
Some people use Project Lombok to add getters and setters automatically. Make sure that Lombok does not generate any particular constructor for such a type, as it is used automatically by the container to instantiate the object.
With "such a type" ConfigurationProperties is referred in Externalized Configuration (one of my favorite chapters;) More Exact: 2.8.1. JavaBean properties binding, at the bottom of second "Note!" ;)
So this could be a reason (for strange behavior).

How to Pass #Value to a #Component for Spring Unit Testing

Im writing unit tests for services, controllers, etc however theres is a #Component that has the following values
#Component
Public class myclass
#Autowired
Private MyTemplate myTemplate
#Value("$someString")
Private String someString
#PostConstruct
Public void loadString()
...
How would I manually load values into the #Values? I have tried with Mocks, TestPropertySource, ReflectionTestUtils, among other ways found around
You can inject the #Value in test class by ReflectionTestUtils. Load container only in case of Controllers. For writing test cases for services and dao you don't need to load the spring container.
public class TestClass{
private #InjectsMock ServiceClass service;
#BeforeAll
public void setUp(){
ReflectionTestUtils.setField(service, "someString", "someValue");
}
//your test cases over here.
}
I can immediately think of two options
1) You could define $someString in your test/resources/test.properties
#RunWith(SpringRunner.class)
#TestPropertySource(locations="classpath:test.properties")
public class ClassTest {
}
2) do it manually
#RunWith(SpringRunner.class)
public class ClassTest {
#Autowired
private MyClass miclass;
#Before
public void setupObject() {
miclass.setProperty("someting");
}
}

Spring Boot - Autowired MongoClient

I got pretty confused now, I would like to use #Autowired MongoClient attribute in one of my Controller classes, but without success. The tricky part of it is that #Autowired is working from my #RestController.
#RestController
public final class WebController {
/** mongoClient */
#Autowired
private MongoClient mongoClient; <- here it's working ...
...
}
but:
#Controller
public final class MongoUsersDAO {
/** mongoClient */
#Autowired
private MongoClient mongoClient; <- not working ...
...
}
here I get null.
I do not think that the problem would be the component scan while my #SpringBootApplication is located at x.y.z, my #RestController at x.y.z.t and my #Controller at x.y.z.k packages, hence booth of them should be scanned by Spring.
(The Eclipse also marks my #Controller as a Spring class)
What else could be the problem then ?
Note:
If I add this to my #Controller it's working fine but the #Autowired still wount work:
#PostConstruct
public void init() {
System.out.println("INIT");
}
Note: In the mentioned MongoUsersDAO the autowired thing is not working at all, I've tried to get a simple property as well from the application.properties, without success.
Your problem occured because you have called new MongoUserDAO() inside your WebController class as you mentioned in the comment below your question. If you instantiate an object by hand and you have field annotated with #Autowired then this field won't be instantiated with expected instance.
Inject MongoUsersDAO directly to your WebController class as shown below, Spring will handle injecting MongoClient to MongoUserDAO class for you.
WebController :
#RestController
public final class WebController {
/** Service/Repository class*/
#Autowired
private MongoUsersDAO dao;
#GetMapping("/all")
public String getAll(){
dao.callSomeMethod();
}
}
MongoUsersDAO:
#Repository
public final class MongoUsersDAO {
/** mongoClient */
#Autowired
private MongoClient mongoClient;
...
}

how to autowire spring beans from a non-bean object created at runtime?

I have some Jpa repositories and several Entity class. I need to create a helper object for one of my Entity. Inside that helper I use #Autowire to access the Jpa repositories.
#Entity
class A {
#Transient
Helper helper;
...
}
class Helper {
A a;
#Autowired
CRepository repo;
public Helper(A a) {
this.a = a;
}
}
However, the repo is always null. I've tried using SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this) and #Configurable, but both of them failed. Can anybody provide some hint for me?
BTW, A is instantiated inside a rest controller.
Thanks!.
You can use a BeanUtil class to get any bean that created in Springl
#Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Then you can get the bean.
MyBean obj = BeanUtil.getBean(MyBean.class);
Use constructor injection instead of field injection; this is a best practice all the time anyway. Then it's trivial to inject your A into the controller and pass it as a constructor argument.
#Configurable annotation works fine, but you need to use #EnableSpringConfigured annotation in any configuration class in order to make it work. Read my answer in other stackoverflow post: spring autowiring not working from a non-spring managed class
Entity class should not contain any helpers, even if transient. For a clean design you need to separate concerns, so the entity should not be aware of your business logic. I cannot help you more since I don't know which is the goal of that helper, but here you have other alternatives:
ALTERNATIVE 1 (based on your description seems that helper is an stateful bean, so it is not candidate to be a #Service, which I personally think it should be)
#Controller
public MyController {
#RequestMapping(...)
public void processRequest() {
A a = new A();
...
Helper helper = new Helper(a); // CRepository is successfully autowired
}
}
#Configurable(autowire = Autowire.BY_TYPE)
public class Helper {
A a;
#Autowired
CRepository repo;
}
#Configuration
#EnableSpringConfigured
public Application {
...
}
ALTERNATIVE 2 (make your Helper class stateless so that spring is aware of your beans without the need of extra stuff like #Confgurable/#EnableSpringConfigured)
#Controller
public MyController {
#Autowired Helper helper; // CRepository is correctly autowired
#RequestMapping(...)
public void processRequest() {
A a = new A();
...
helper.doSomething(a);
}
}
#Service
public class Helper {
// A a; remove dependency to A to make it stateless
#Autowired
CRepository repo;
public Helper() {
}
public void doSomething(A a) {
...
repo.save(a);
}
}
You cannot autowire nothing in your Helper class because it isn't managed by Spring.
You can use this approach:
public class HelperManager {
#Autowired
private ApplicationContext context;
public Helper getHelper(A a) {
return context.getBean(Helper.class, a);
}
Configure Helper to be a prototype bean:
#Configuration
public class MyConfiguration {
#Bean
public HelperManager helperManager() {
return new HelperManager();
}
#Bean
#Scope("prototype")
public Helper helper(A a) {
return new Helper(a);
}
}
And finally in your controller:
#Controller
public class MyController {
#Autowired
private HelperManager helperManager;
public someMethodWhereToInstanceYourHelper(A a) {
...
Helper helper = helperManager.getHelper(a);
...
}
}

How to access Spring context in jUnit tests annotated with #RunWith and #ContextConfiguration?

I have following test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/services-test-config.xml"})
public class MySericeTest {
#Autowired
MyService service;
...
}
Is it possible to access services-test-config.xml programmatically in one of such methods? Like:
ApplicationContext ctx = somehowGetContext();
This works fine too:
#Autowired
ApplicationContext context;
Since the tests will be instantiated like a Spring bean too, you just need to implement the ApplicationContextAware interface:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/services-test-config.xml"})
public class MySericeTest implements ApplicationContextAware
{
#Autowired
MyService service;
...
#Override
public void setApplicationContext(ApplicationContext context)
throws BeansException
{
// Do something with the context here
}
}
For non xml needs, you can also do this:
#RunWith(SpringJUnit4ClassRunner.class)
/* must provide some "root" for the app-context, use unit-test file name to the context is empty */
#ContextConfiguration(classes = MyUnitTestClass.class)
public class MyUnitTestClass implements ApplicationContextAware {
If your test class extends the Spring JUnit classes
(e.g., AbstractTransactionalJUnit4SpringContextTests or any other class that extends AbstractSpringContextTests), you can access the app context by calling the getContext() method.
Check out the javadocs for the package org.springframework.test.
It's possible to inject instance of ApplicationContext class by using SpringClassRule
and SpringMethodRule rules. It might be very handy if you would like to use
another non-Spring runners. Here's an example:
#ContextConfiguration(classes = BeanConfiguration.class)
public static class SpringRuleUsage {
#ClassRule
public static final SpringClassRule springClassRule = new SpringClassRule();
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
#Autowired
private ApplicationContext context;
#Test
public void shouldInjectContext() {
}
}

Resources