I am trying to mock the bean having scope prototype.
package com.aks.operation;
#Scope(value="prototype")
public class Operation{
public int sum(int a,int b){
return a+b;
}
}
#Service
public class UseOperation{
#Autowired
private ApplicationContext context;
public void doOperation() throws Exception{
Operation opr = context.getBean(Operation.class); // this will return the prototype instance
..
..
}
}
//Junit
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:/app-context.xml" })
#ActiveProfiles("DEV")
#TestExecutionListeners(listeners = { DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class })
#ComponentScan(basePackages = { "com.aks.operation" })
public class TestOperation{
#InjectMocks
private Operation operation;
#Autowired
private UseOperation useOperation;
#Before
public void before() throws Exception{
MockitoAnnotations.initMocks(this);
when(operation.sum(1,2)).thenReturn(3);
}
#Test
public void doOperation() throws Exception{
useOperation.doOperation();// this function will create the prototype instance, and that instance should be mine for which I did mocking
}
}
Please suggest what approach should I take to test the prototype instances.
It's the context.getBean(Class<?>) which is always returning me the new instances.
Thanks.
Related
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;
}
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
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");
}
}
I have simple spring boot web service, where for configuration I use .properties files. As example for spring-mail configuration, I have separate file mailing.properties located in src/main/resources/config/ folder.
in main application I include it using:
#PropertySource(value = { "config/mailing.properties" })
The problem appears when it comes to tests, I would like to use the same properties from this file, but when i try to use it, I get fileNotFaundExeption.
Question is:
Should I have separate resources in my src/test folder, or it is possible to access resources from src/main folder, if yes, how?
UPDATE added sources
test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#TestPropertySource("classpath:config/mailing.properties")
public class DemoApplicationTests {
#Autowired
private TestService testService;
#Test
public void contextLoads() {
testService.printing();
}
}
service class:
#Service
public class TestService
{
#Value("${str.pt}")
private int pt;
public void printing()
{
System.out.println(pt);
}
}
main app class:
#SpringBootApplication
#PropertySource(value = { "config/mailing.properties" })
public class DemoApplication {
public static void main(String[] args)
{
SpringApplication.run(DemoApplication.class, args);
}
}
You can use #TestPropertySource annotation in your test class.
For example you have this attribute in your mailing.properties file:
mailFrom=fromMe#mail.com
Just annotate #TestPropertySource("classpath:config/mailing.properties") on your test class.
You should be able to read out the property for example with the #Value annotation.
#Value("${fromMail}")
private String fromMail;
To avoid annotate this annotation on multiple test classes you can implement a superclass or meta-annotations.
EDIT1:
#SpringBootApplication
#PropertySource("classpath:config/mailing.properties")
public class DemoApplication implements CommandLineRunner {
#Autowired
private MailService mailService;
public static void main(String[] args) throws Exception {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... arg0) throws Exception {
String s = mailService.getMailFrom();
System.out.println(s);
}
MailService:
#Service
public class MailService {
#Value("${mailFrom}")
private String mailFrom;
public String getMailFrom() {
return mailFrom;
}
public void setMailFrom(String mailFrom) {
this.mailFrom = mailFrom;
}
}
DemoTestFile:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = DemoApplication.class)
#TestPropertySource("classpath:config/mailing.properties")
public class DemoApplicationTests {
#Autowired
MailService mailService;
#Test
public void contextLoads() {
String s = mailService.getMailFrom();
System.out.println(s);
}
}
I have a controller like this
#Path("/")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class AccountController implements CRUDController<Long, Account> {
private AccountDao accountDao;
private AccountService accountService;
#Inject
public AccountController(AccountDao accountDao, AccountService accountService) {
this.accountDao = accountDao;
this.accountService = accountService;
}
...
I'm injecting AccountDao and AccountService using
ResourceConfig config = new ResourceConfig()
.packages("controller", "exception")
.register(new MyDIBinder());
Where MyDIBinder is contains all the bindings (e.g
AccountDaoImpl accountDaoImpl = new AccountDaoImpl();
bind(accountDaoImpl).to(AccountDao.class);
)
Now I want to write a unit test for this controller, is it possible to inject the whole AccountController instance with all of it's transitive dependencies into the test?
Something like
#Inject
AccountController accountController;
You can use the main IoC container, and just explicitly inject the test class. Jersey uses HK2 as its DI framework, and its IoC container is the ServiceLocator, which has a method inject(anyObject) that can inject any objects with dependencies that are in its registry.
For example you could do something like
public class InjectionTest {
#Inject
private TestController controller;
#Before
public void setUp() {
final Binder b = new AbstractBinder() {
#Override
public void configure() {
bindAsContract(TestController.class);
}
};
final ServiceLocator locator = ServiceLocatorUtilities.bind(new TestBinder(), b);
locator.inject(this);
}
#Test
public void doTest() {
assertNotNull(controller);
String response = controller.get();
assertEquals("Hello Tests", response);
}
}
The ServiceLocatorUtilities class is a helper class that allows us to easily create the ServiceLocator, and then we just call inject(this) to inject the InjectionTest.
If it seems repetitive to do this for all your controller tests, you may want to create an abstract base test class. Maybe something like
public abstract class AbstractControllerTest {
protected ServiceLocator locator;
private final Class<?> controllerClass;
protected AbstractControllerTest(Class<?> controllerClass) {
this.controllerClass = controllerClass;
}
#Before
public void setUp() {
final AbstractBinder binder = new AbstractBinder() {
#Override
public void configure() {
bindAsContract(controllerClass);
}
};
locator = ServiceLocatorUtilities.bind(new TestBinder(), binder);
locator.inject(this);
}
#After
public void tearDown() {
if (locator != null) {
locator.shutdown();
}
}
}
Then in your concrete class
public class TestControllerTest extends AbstractControllerTest {
public TestControllerTest() {
super(TestController.class);
}
#Inject
private TestController controller;
#Test
public void doTest() {
assertNotNull(controller);
assertEquals("Hello Tests", controller.get());
}
}
If you spent some more time, I'm sure you could come up with a better abstract test class design. It was the first thing that came to mind for me.
Note: For anything request scoped, you mayb need to just mock it. When running the unit tests, there is no request context, so the test will fail.
See Also:
Using Jersey's Dependency Injection in a Standalone application
HK2 documentation
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
#BeforeClass
public static void doTest() {
ServiceLocator serviceLocator = ServiceLocatorUtilities.bind(new AbstractBinder() {
#Override
protected void configure() {
bindAsContract(YourClass1.class);
bindAsContract(YourClass2.class);
bindAsContract(YourClass3.class);
}
});
YourClass1 yourClass1 = serviceLocator.getService(YourClass1.class);
...