I have the following scenario in Spring :
public class ClassA{
#Autowired
private ClassB classB;
}
I'm using (Autowiring to be more precise) ClassA in my Test class. But what I'd like to do somehow is to modify ClassB just for my Junit, so with that, When ClassA is autowired in my test class, it loads the modified ClassB (instead of the original one).
Is there a way to achive that?
Can't think of another way to do this without Bean Configuration.
You can configure this in 2 ways:
First:
#Configuration
public class AppConfig {
#Bean
public ClassB classB() {
return new ClassB() {
// this is a subclass that inherits everything from ClassB, so override what you want here
}
}
}
Second: (taken from here)
#RunWith(SpringRunner.class)
#SpringBootTest
public class SomeTest {
// do this if you only want the modified classB in 1 place
#Configuration
static class TestConfig {
#Bean
public ClassB classB () {
return new ClassB() {
// same as the first
}
}
}
#Test
public void testMethod() {
// test
}
}
Finally, you could create a new interface ClassB and ClassBImpl in your main folder and ClassBTestImpl in your test folder. You still need to use one of the configuration.
Related
for example ,there are 2 beans.
parentClass is a generics class
// parentClass
#Service
public class ParentService<T> {
public ParentService(){
System.out.println("ParentService: class"+this.getClass()+" "+this);
}
}
//subClass
subClass extends parentClass and indicates that the generic is a "string" type
#Service
public class ChildService extends ParentService<String> {
public ChildService(){
System.out.println("ChildService: class"+this.getClass()+" "+this);
}
}
// TestCase
autowired the subClass , parentClass that generic is String
#RunWith(SpringRunner.class)
#SpringBootTest
public class OneBeanApplicationTests {
#Autowired
private ChildService childService;
#Autowired
private ParentService<String> stringParentService;
#Test
public void contextLoads() {
System.out.println(childService == stringParentService);// true
}
}
the answer is :TRUE
I am confused about that
=====================================
if i edit the test class
#Autowired
private ParentService parentService;
......
parentService==stringParentService; // false
this is the example project: https://github.com/AshameL/WhyIsSameBean
you can pull it and run the test class
I created one controller class and tried this
#Autowired
ParentService<String> stringParentService;
#Autowired
ChildService childService;
#Autowired
ParentService parentService; // Object class
#GetMapping("/test123")
public void contextLoads() {
System.out.println(childService.hashCode()+" : "+stringParentService.hashCode());
System.out.println(childService == stringParentService);
System.out.println(childService.equals(stringParentService));
System.out.println(Integer.toHexString(System.identityHashCode(childService)));
System.out.println(Integer.toHexString(System.identityHashCode(stringParentService)));
System.out.println("=====================");
System.out.println(parentService == stringParentService);
System.out.println(parentService.hashCode()+" : "+stringParentService.hashCode());
}
OUTPUT:
563182512 : 563182512
true
true
21917bb0
21917bb0
=====================
false
196061929 : 563182512
This is expected as when we have ParentService<String>, since hashcode is not overriden both Parent and Child class share same Object
In case of ParentService default type pass is Object thus different hashcode and so different Object.
Edit 1:
During Server Start up I can see following log
ParentService: classclass com.example.demo.service.ChildService com.example.demo.service.ChildService#2dc6b83f
ChildService: classclass com.example.demo.service.ChildService com.example.demo.service.ChildService#2dc6b83f
ParentService: classclass com.example.demo.service.ParentService com.example.demo.service.ParentService#349131e3
and when I do same with below code
public static void main(String[] args) {
ParentService parentService = new ChildService();
ParentService parentService1 = new ParentService();
}
OUTPUT:
ParentService: classclass com.example.demo.service.ChildService com.example.demo.service.ChildService#1d44bcfa
ChildService: classclass com.example.demo.service.ChildService com.example.demo.service.ChildService#1d44bcfa
ParentService: classclass com.example.demo.service.ParentService com.example.demo.service.ParentService#266474c2
which conclude that
#Autowired
ChildService childService;
#Autowired
ParentService<String> stringParentService;
are instance of ChildService class due to extend.
In java "==" compares object references.
Since ChildService extends from ParentService they essentially might have a reference to the same object in memory, hence you probably get true when comparing them using the "==" sign.
The default scope in Spring is singleton, so you will get the same instance of ChildService with autowiring.
I have an integration test class annotated with #SpringBootTest which starts up the full application context and lets me execute my tests. However I am unable to #Autowired beans into the test class itself. Instead I get an error:
No qualifying bean of type 'my.package.MyHelper' available".
If I do not #Autowire my helper class, but keep the code directly inside the setUp function, the test works as expected.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class)
public class CacheControlTest {
#Autowired
private MyHelper myHelper;
#Before
public void setUp() {
myHelper.doSomeStuff();
}
#Test
public void test1() {
// My test
}
}
How can I make use of Spring autowiring inside the test class while also using #SpringBootTest?
Following #user7294900 advice below, creating a separate #Configuration file and adding this at the top of CacheControlTest works:
#ContextConfiguration(classes = { CacheControlTestConfiguration.class })
However is there any way of keeping the configuration inside the CacheControlTest class itself? I have tried adding inside my test class:
public class CacheControlTest {
#TestConfiguration
static class CacheControlTestConfiguration {
#Bean
public MyHelper myHelper() {
return new MyHelper();
}
}
}
And
public class CacheControlTest {
#Configuration
static class CacheControlTestConfiguration {
#Bean
public MyHelper myHelper() {
return new MyHelper();
}
}
}
But they do not seem to have any effect. I still get the same error. The same configuration block works when placed in an separate file as mentioned above though.
Add ContextConfiguration for your Test Class:
#ContextConfiguration(classes = { CacheControlTestConfiguration.class })
In a Spring Boot application I am working on, I have a class which is not annotated as bean (#Component), but contains an autowired field:
public class One{
#Autowired
private Two x;
public getX(){
return x;
}
}
In the configuration xml of the Spring application the class One is marked as bean which makes that the variable x gets initialized when I run the application.
Now I have written a test that doesn't seem to use the spring xml configuration. So I tried to do it manually:
#RunWith(SpringRunner.class)
public class Test{
#Autowired
One y;
#Test
public void checkOne(){
System.out.println(y.getX()); //null
}
}
How can I make Spring inject the correct code so that x is not null in my test?
Just tell the test what config to use:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:applicationContext_test.xml" })
public class Test{
#Autowired
One y;
#Test
public void checkOne(){
System.out.println(y.getX()); //null
}
}
See here for doc
Alernative to #Essex Boy approach:
use a custom configuration in your test:
#RunWith(SpringRunner.class)
public class TestClass {
#Configuration
#ComponentScan
static class ConfigurationClass {
#Bean
public One makeOne() {
return new One();
}
}
#Autowired
One y;
#Test
public void checkOne(){
System.out.println(y.getX());
}
}
Essex Boy's approach runs an "integration test" because it starts up Spring for the test.
Usually for Unit Tests, you want to mock your depencies; those can be "autowired".
#RunWith(MockitoJUnitRunner.class) // necessary for the annotations to work
public class YourTest {
// this is a mock
#Mock
private Two mockedTwo;
#InjectMocks
// this is automatically created and injected with dependencies
private One sut;
#Test
public void test() {
assertNotNull(sut.getX());
sut.doStuff();
verify(mockedTwo).wasCalled();
}
}
I have a java interface say ITest implemented by two classes Class1 and Class2. Now I have a factory class which I use to decide which implementation to return. like
if(something)
return new Class1()
else
return new Class2()
Problem is that I have autowired field in Class1 which doesn't get instantiated,, but the same autowiring works in other classes which were instantiated via autowiring.
Here is code for Class1
#Componene
public Class1 implements ITest{
#Autowired
private SomeClass obj;
}
Not sure how to get around this problem. As autowiring for SomeClass works fine in Other classes.
Inject applicationContext in your factory class and use it for bean creation:
#Autowired private ApplicationContext applicationContext;
......
......
if(something){
return applicationContext.getBean("com.xyz.Class1");
}
......
......
OR you can use #Configurable on top of Class1 and Class2. This needs AspectJ weaving to be enabled. Spring will then magically create beans when you use new.
It strongly depends of what exactly do you need, but you can:
Define the object returned by factory as prototype-scope and inject it to singleton, for more details look at: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-scopes-sing-prot-interaction
Then whole magic is done by Spring
If you just want to switch between two different stateless implementations you can inject both of them to factory and return an existing bean
#Compoment class Factory {
#Autowired
Class1 class1Instance;
#Autowired
Class2 classInstance;
...
if(something)
return class1Instance;
else
return class2Instance;
...
}
}
But please make sure that both injected classes have no state.
Make factory managed by Spring, both classes not-managed by Spring and inject dependencies manually:
public Class1 implements ITest{
private SomeClass obj;
public Class1(SomeClass obj) {
this.obj=obj;}
}
and in factory:
#Autowired
private SomeClass obj;
...
if(something)
return new Class1(obj);
else
return new Class2(obj);
At first glance looks strange, by that way you can e.g. separate domain and application/infrastructure parts.
You could use your Main Configuration class which is annotated with #SpringBootApplication or #Configuration.
Example:
#SpringBootApplication
public class YourApp {
public static void main(String[] args) {
SpringApplication.run(YourApp.class, args);
}
#Autowired
private CsvBeanRepository repo;
#Bean
public InterfaceSome some() {
if(something) {
return new Some(repo);
} else {
return new Some2(repo);
}
}
}
inside a #SpringBootApplication class or a #Configuration class you can #Autowire your classes normally and use them as parameters for your factory.
I would suggest to use #value annotation for this scenario.
Example
MyClass{
Object getObjectFromFactory()
{
if(something)
return new Class1()
else
return new Class2()
}
}
then you can use it like below
#Value("#{MyClass.getObjectFromFactory}")
private Object myObject;
I have a class like this:
#Service("someClient")
public class SomeClient {
#Value{some.value}
private String someValue;
public void someMethod() {
return someValue;
}
}
And a test like this:
#ContextConfiguration(locations = "classpath:/some/where/testApplicationContext.xml")
#RunWith(SpringJUnit4ClassRunner.class)
public class SomeClientTest extends TestCase {
#Value{some.value}
private String someValueTest;
#Test
public void shouldWork() {
...
someClient.someMethod()
...
}
}
When the wider application is running, the field someValue inside the SomeClient class is populated from a properties file referenced from testApplicationContext.xml. When I run the test in debug mode I can see that someValueTest is populated in the test, but when the test calls the class under test, the value is not populated.
I could use some advice! Obviously I can change the visibility of the field in the class, or provide a setter, however I would like to avoid that if possible. If it isn't, please advise.
In order to populate fields with #Value annotation in your test you need to configure PropertySourcesPlaceholderConfigurer.
Add the following to your test:
#Configuration
public static class Config {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
To read the values from test property file you can add
#TestPropertySource(locations="classpath:test.properties") to your Test class declaration
You can use ReflectionTestUtils from org.springframework.test.util.ReflectionTestUtils package to mock any variable, including the ones that access the properties file.
#RunWith(SpringJUnit4ClassRunner.class)
public class SomeClientTest extends TestCase {
private SomeClient someClient;
#Test
public void shouldWork() {
//Initialize someClient
someClient = new SomeClient();
ReflectionTestUtils.setField(someClient, "variable name", "the variable value");
someClient.someMethod()
...
}
}