How many times should SpringJUnit4ClassRunner initialize it's context? - java

It is said in manual, that
The Test annotation tells JUnit that the public void method to which
it is attached can be run as a test case. To run the method, JUnit
first constructs a fresh instance of the class then invokes the
annotated method. Any exceptions thrown by the test will be reported
by JUnit as a failure. If no exceptions are thrown, the test is
assumed to have succeeded.
which may mean, that for each #Test method the context should be initialized again. This is also confirmed in this answer: https://stackoverflow.com/a/1564309/258483
Simultaneously, I see opposite in my experiment:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringJUnit4ClassRunnerDemo._Config.class)
public class SpringJUnit4ClassRunnerDemo {
public static class Bean1 {
{
System.out.println("Bean1 constructor called");
}
}
public static class Bean2 {
{
System.out.println("Bean2 constructor called");
}
private Bean1 bean1;
public Bean1 getBean1() {
return bean1;
}
#Autowired
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
System.out.println("Bean2.bean1 property set");
}
}
#Configuration
public static class _Config {
#Bean
public Bean1 bean1() {
return new Bean1();
}
#Bean
public Bean2 bean2() {
return new Bean2();
}
}
#Autowired
private Bean1 bean1;
#Autowired
private Bean2 bean2;
#Test
public void testBean1() {
assertNotNull(bean1);
System.out.println("testBean1() done");
}
#Test
public void testBean2() {
assertNotNull(bean2);
assertSame(bean2.getBean1(), bean1);
System.out.println("testBean2() done");
}
}
This code outputs
Bean1 constructor called
Bean2 constructor called
Bean2.bean1 property set
testBean1() done
testBean2() done
which may mean, that context is not initialized second time before second test.
What is actual and correct behavior and how to control it?

If you want the Spring context reloaded between test methods, you need to use the #DirtiesContext annotation: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/annotation/DirtiesContext.html

Related

Beans are null when unit testing spring-retry with SpringRunner

I am trying to write unit tests for a class having spring retry using the springRunner. But my #Autowired beans are null . Could you please let me know what I did wrong?
Below is my test class
#RunWith(SpringRunner.class)
#ContextConfiguration
public class DeltaHelperTest {
#Autowired
private DeltaHelper deltaHelper;
#Before
public void setUp() { System.setProperty("delta.process.retries", "2"); }
#After
public void validate() { validateMockitoUsage(); }
#Test
public void retriesAfterOneFailAndThenPass() throws Exception {
when(deltaHelper.restService.call(any(), any())).thenThrow(new HttpException());
deltaHelper.process(any(),any());
verify(deltaHelper, times(2)).process(any(), any());
}
#Configuration
#EnableRetry
#EnableAspectJAutoProxy(proxyTargetClass=true)
#Import(MockitoSkipAutowireConfiguration.class)
public static class Application {
#Bean
public DeltaHelper deltaHelper() {
DeltaHelper deltaHelper = new DeltaHelper();
deltaHelper.myStorageService= myStorageService();
deltaHelper.restService = restService();
return deltaHelper;
}
#Bean
public MyStorageService myStorageService() {
return new MyStorageService();
}
#Bean
public MyRestService restService() {
return new MyRestService();
}
#Bean
public MyRepo myRepository() {
return mock(MyRepo.class);
}
}
#Configuration
public static class MockitoSkipAutowireConfiguration {
#Bean MockBeanFactory mockBeanFactory() {
return new MockBeanFactory();
}
private static class MockBeanFactory extends InstantiationAwareBeanPostProcessorAdapter {
#Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return !mockingDetails(bean).isMock();
}
}
}
}
Here test service is null on deltaHelper object .
MyRepo.class is mocked as it has some more #autowired bean reference
Attaching other classes here
#Component
public class DeltaHelper {
#Autowired
MyRestService restService;
#Autowired
MyStorageService myStorageService;
#NotNull
#Retryable(
value = Exception.class,
maxAttemptsExpression = "${delta.process.retries}"
)
public String process(String api, HttpEntity<?> entity) {
return restService.call(api, entity);
}
#Recover
public String recover(Exception e, String api, HttpEntity<?> entity) {
myStorageService.save(api);
return "recover";
}
}
#Service
public class MyStorageService {
#Autowired
MyRepo myRepo;
#Async
public MyEntity save(String api) {
return myRepo.save(new MyEntity(api, System.currentTimeMillis()));
}
}
public class MyRestService extends org.springframework.web.client.RestTemplate {
}
Thank you
Tried MockitoJUnitRunner, But found that #Retryable works only when running with Spring
I'm not sure why you are trying to test framework functionality such as retry. Generally, you can assume that framework components have been tested thoroughly by the framework authors.
Ignoring that, I can see at least two problems:
deltaHelper is not a mock, but your SUT, yet you try to set up method calls. If you mock your SUT, you are no longer testing your class, you are testing the mock. If you want your call to fail, don't mock the call, but mock its dependencies (e.g. MyRestService restService) and have calls on the dependency throw an exception.
You pass ArgumentMatchers.any() in your real method call (the "act" part), but any() unconditionally returns null (not some magic object). If you want to act on your SUT, you must pass real values. any is for setting up mocks or verifying calls on mocks.
For completeness' sake, here's the source of any():
public static <T> T any() {
reportMatcher(Any.ANY);
return null;
}

How to mock double autowired class

In the following case, can I test "MainServiceImpl"?
【What I want to do】
・Test target object is "MainServiceImpl".
・SubMainServiceImpl is used without mock.
・SubSubMainServiceImpl.subSubSayHello method is used with mock. 
You may say "double autowired class is not recommended to mock." I know, but I want to know the above test is technically possible.
//Test Target Object
#Service
public class MainServiceImpl implements MainService {
#Autowired
private SubMainService subMainService;
#Override
public String mainSayHello() {
return "MainSayHello. Also..." + subMainService.subSayHello();
}
}
//MainServiceImpl depends on this class.
#Service
public class SubMainServiceImpl implements SubMainService {
#Autowired
private SubSubMainService subSubMainService;
#Override
public String subSayHello() {
return "SubSayHello. Also..." + subSubMainService.subSubSayHello();
}
}
//SubSubServiceImpl depends on this class.
#Service
public class SubSubMainServiceImpl implements SubSubMainService {
#Override
public String subSubSayHello() {
return "SubSubSayHello";
}
}
As a side note, I can mock direct-autowired class.
How can I mock "double" autowired class?
//Direct(not double) autowired class is mocked.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MockMainServiceTest {
#InjectMocks
MainServiceImpl mainService;
#Mock
SubMainService subMainService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void SubMainService_Mocked(){
doReturn("SubMainService_Mocked").when(subMainService).subSayHello();
Assert.assertThat(mainService.mainSayHello() ,is("MainSayHello. Also...SubMainService_Mocked"));
}
}
Technology
SpringBoot 2.5.4
JUnit 4.2
Java 1.8

JUNIT Autowired instance is always null

I am trying to write test case for the below class where everytime myConfig instance is coming as null. Is there any way to pass the autowired instance.
public class MyClass {
#Autowired
MyConfig myConfig ;
public Properties getUnAckMessage(String queueName) {
Properties prop=new Properties()
URL url = new URL(StringUtils.join(myConfig.getQueueHost(),
myConfig.getQueueURL(),myConfig.getQueueVm(),queueName));
return prop;
}
public Properties request(String queue) {
return getUnAckMessage(queue);
}
}
public class Main {
public void method() {
MyClass myClass=new MyClass();
myClass.getUnAckMessage("test");
}
}
Test case
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
#MockBean
MyConfig myConfigReader;
#Test
public void testMyClass() {
MyClass propertiesExchangeManager1 = new MyClass ();
propertiesExchangeManager1.request("test");
}
}
You must activate Spring for your test if you want Spring to autowire. For example:
#RunWith(SpringRunner.class)
public class Test {
#Autowired private MyClass myClass
#Test
public void test() {
///...
}
}
If you instantiate the class MyClass by yourself, Spring cannot inject the needed classes. You should modify your test like this:
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
#MockBean
MyConfig myConfigReader;
#Autowired
MyClass propertiesExchangeManager1;
#Test
public void testMyClass() {
propertiesExchangeManager1.request("test");
}
}

Injecting Mocked objects using FactoryBean

I have a FactoryBean (Spring) defined as follows:
public class AMockFactoryBean extends EasyMockFactoryBean<A>{
public AMockFactoryBean() {
super(A.class);
}
#Override
public A getObject() throws Exception {
MockA a= new MockA();
a.setB(createMock(B.class));
return new MockA();
}
}
The class A has an object of type B autowired:
public abstract class A {
#Autowired
protected B b;
}
MockA implements a few abstract classes and EasyMockFactoryBean utilizes the Spring FactoryBean method.
In my app.xml config I have:
<bean id="mockedA" class="AMockFactoryBean" />
My test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:testContext.xml")
public class ATest {
#Autowired
private A mockedA;
}
Result: mockedA in ATest is autowired correctly, but the autowired field A.b has been set to null by Spring. In debug mode I see how getObject() in AMockFactoryBean is called and how mockedA is given a Mock instance of EasyMock. But when the debugger jumps into the ATest class, mockedA.b is null. Why?
You return return new MockA(); instead of a. Your code should be
#Override
public A getObject() throws Exception {
MockA a= new MockA();
a.setB(createMock(B.class));
return a;
}

Spring dependency injection with #Autowired annotation without setter

I'm using Spring since a few months for now, and I thought dependency injection with the #Autowired annotation also required a setter for the field to inject.
So, I am using it like this:
#Controller
public class MyController {
#Autowired
MyService injectedService;
public void setMyService(MyService injectedService) {
this.injectedService = injectedService;
}
...
}
But I've tried this today:
#Controller
public class MyController {
#Autowired
MyService injectedService;
...
}
And oh surprise, no compilation errors, no errors at startup, the application is running perfectly...
So my question is, is the setter required for dependency injection with the #Autowired annotation?
I'm using Spring 3.1.1.
You don't need a setter with the #Autowired, the value is set by reflection.
Check this post for complete explanation How does Spring #Autowired work
No, if Java security policy allows Spring to change the access rights for the package protected field a setter is not required.
package com.techighost;
public class Test {
private Test2 test2;
public Test() {
System.out.println("Test constructor called");
}
public Test2 getTest2() {
return test2;
}
}
package com.techighost;
public class Test2 {
private int i;
public Test2() {
i=5;
System.out.println("test2 constructor called");
}
public int getI() {
return i;
}
}
package com.techighost;
import java.lang.reflect.Field;
public class TestReflection {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class<?> class1 = Class.forName("com.techighost.Test");
Object object = class1.newInstance();
Field[] field = class1.getDeclaredFields();
field[0].setAccessible(true);
System.out.println(field[0].getType());
field[0].set(object,Class.forName(field[0].getType().getName()).newInstance() );
Test2 test2 = ((Test)object).getTest2();
System.out.println("i="+test2.getI());
}
}
This is how it is done using reflection.

Categories