I need to intercept methods from a interface, and found this implementation of MethodInterceptor, which I tested on a new spring app and worked.
The problem is, I can't seem to get it working on the spring application I need it to.
#Configuration
public class TestMethodConfig {
#Autowired
private TestService testService;
#Bean
#Primary
public ProxyFactoryBean testProxyFactoryBean() {
ProxyFactoryBean testProxyFactoryBean = new ProxyFactoryBean();
testProxyFactoryBean.setTarget(testService);
testProxyFactoryBean.setInterceptorNames("testMethodInterceptor");
return testProxyFactoryBean;
}
}
#Service
public class TestServiceImpl implements TestService{
#Override
public void testMethod(String test) {
System.out.println("testService String");
}
}
public interface TestService{
void testMethod(String test);
}
#RestController
public class Controller {
#Autowired
private TestService testProxyFactoryBean;
#GetMapping(value = "/test")
public void test(){
testProxyFactoryBean.testMethod("valor");
}
}
#Component
public class TestMethodInterceptor implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before method");
System.out.println("invocation: " + Arrays.toString(invocation.getArguments()));
Object retVal = invocation.proceed();
System.out.println("after method");
return retVal;
}
}
I used Spring Actuator to check the beans relations, and I found that the #Autowired TestService on Controller should be getting assigned to testProxyFactoryBean, but its getting assigned to the TestServiceImpl bean instead, so I believe there is a problem creating the proxy.
In short
I don't know how/why it was:
on a new spring app and worked.
but:
I can't seem to get it working on the spring application I need it to.
..can probably be fixed!
Make it consistent
Or:
#Configuration
public class TestMethodConfig {
#Autowired
private TestService testService;
}
...
// !!
public class TestServiceImpl implements TestService{
#Override
public void testMethod(String test) {
System.out.println("testService String");
}
}
...
#Service // !!!
public interface TestService{
void testMethod(String test);
}
...
#RestController
public class Controller {
#Autowired
private TestService testProxyFactoryBean;
...
Or: Impl!
(Use Interface and Impl consistently!)
In Detail
6.4. Using the ProxyFactoryBean to Create AOP Proxies
esp. Proxying Interfaces.
So with "least impact" (and java config), it should be:
#Configuration
public class TestMethodConfig {
// !!! Impl from component-scan (#Service), NOT interface:
#Autowired
private TestServiceImpl testServiceImpl; // or define custom, or "inline"...
#Bean
#Primary // only if you need it, better would be: distinct!
public ProxyFactoryBean testProxyFactoryBean() {
ProxyFactoryBean testProxyFactoryBean = new ProxyFactoryBean();
// !!! set proxyInterface as documented:
testProxyFactoryBean.setProxyInterface(TestService.class);
testProxyFactoryBean.setTarget(testServiceImpl);
testProxyFactoryBean.setInterceptorNames("testMethodInterceptor");
// ...
return testProxyFactoryBean;
}
}
..enjoy! ;)
Related
I am having a Repository through which I'm getting data from Elasticsearch. It is working correctly when I send a GEt request.
I need that data from Elasticsearch without sending a GET request for which I've written a Service and annotated that repository in Service class using #Autowired.
Repository
#Repository
public interface RecipeDtoRepository extends
ElasticsearchRepository<RecipeDto, Integer> {}
Controller
#RestController
#RequestMapping("/repo")
public class RecipeRepoController {
#Autowired
public RecipeDtoRepository recipeDtoRepository;
#GetMapping("/all")
public String getRecipes() {
List<RecipeDto> recipe_list = new ArrayList<>();
recipeDtoRepository.findAll().forEach(recipe_list::add);
return recipe_list.toString();
}
Service
#Service
#Component
public class ClusterService implements ApplicationContextAware {
#Autowired
public RecipeDtoRepository recipeDtoRepository;
List<RecipeDto> recipe_list = new ArrayList<>();
new ClusterService(). applicationContext.getBean(RecipeRepoController.class).recipeDtoRepository.findAll().forEach(recipe_list::add);
}
#Override
public void setApplicationContext(ApplicationContext
applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
Problem
when calling the method in controller using GET request, it is working fine but when I call it using Service class, it is throwing NullPointerException which it shouldn't be according to my knowledge. I read an article where it was suggested to get the Bean in Service class which you can see I've done using ApplicationContext but it is throwing that exception. I've tried it without using ApplicationContext and it throws NullPointerException in that case too. I'm working over it for the last two days and unable to find a solution.
#Service
#Configurable
public class ClusterService {
#Autowired
public RecipeDtoRepository recipeDtoRepository;
private String getRecipes() {
List<RecipeDto> recipe_list = new ArrayList<>();
recipeDtoRepository.findAll().forEach(recipe_list::add);
return recipe_list.toString();
}
public static void main(String[] a) {
ClusterService clusterService = new ClusterService();
clusterService.getRecipes();
}}
Remove the contextAware and dont call the instance of repository from the controller since you have injected it yet
just do this
public void addThings(){
this.recipeDtoRepository.findAll().forEach(recipe_list::add)
}
if you have annotated your class using stereotype annotation and you are trying to get bean in main class try this
ApplicationContext context=
SpringApplication.run(Main.class,args);
ClusterService clusterService=context.getBean(ClusterService.class);
clusterService.getRecipes();
Assuming that OP wants to persist a list and use it through out the lifecycle. Here is my recommendation
#Service
public class ClusterService {
#Autowired
public RecipeDtoRepository recipeDtoRepository;
private List<RecipeDto> recipeList = null;
#PostContruct
public void setup() {
recipeList = new ArrayList<>();
recipeDtoRepository.findAll().forEach(recipeList::add);
}
public getReceipeList() {
return recipeList;
}
}
#RestController
#RequestMapping("/repo")
public class RecipeRepoController {
#Autowired
ClusterService clusterService
#GetMapping("/all")
public String getRecipes() {
return clusterService.getReceipeList().toString();
}
}
I'm trying to unit test a camel route. The route under test extends a custom abstract RouteBuilder (I know about favouring composition over inheritance - this is maintenance code). I've set up my test as #Roman Vottner did over here. Everything works (is initialized) until I hit the first abstract class up the hierarchy. It has an #Autowired class which wasn't initialized (is null) even though it was mocked and #Autowired when the test started. Any ideas on how to solve my injection problem?
#RunWith(CamelSpringRunner.class)
#BootstrapWith(CamelTestContextBootstrapper.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = {FooRouteTest.ContextConfig.class})
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class FooRouteTest {
#Configuration
#PropertySource({"classpath:some.properties", "classpath:environment.properties"})
public static class ContextConfig extends CamelConfiguration {
#Bean
public UserServices userServices() {
return mock(UserServices.class);
} //and many more of the like
}
#Autowired
private UserServices userServices; //and all the others too
#Test
public void testAfoo() throws Exception {
//....
template.setDefaultEndpointUri("direct://getTheData");
template.sendBody(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode));
//...
}
}
in the abstract super class while debugging:
#Autowired
public ClientServices clientServices;
//...
String clientNumber=clientServices.getLoggedInNumber(); //clientServices is null and not mocked!
//...
Solved this by explicitly declaring FooRoute as a bean:
#Bean
public FooRoute fooRoute(){
return new FooRoute();
}
#Override
public List<RouteBuilder> routes() {
final List<RouteBuilder> routes = new ArrayList<>();
routes.add(fooRoute());
return routes;
}
I have a problem I'm not able to solve. I have searched on the internet and on Stackoverflow but could not find how to solve the problem.
I want to test a Spring MVC Handler interceptor. This interceptor has a "session" scope bean as a dependency.
I tried to reduce the code as much as possible. Here is the code:
The src part :
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = { "..." })
public class SpringMvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry interceptorRegistry) {
interceptorRegistry.addInterceptor(initializeUserLanguageHandler());
}
#Bean
public InitializeUserLanguageHandler initializeUserLanguageHandler() {
return new InitializeUserLanguageHandler();
}
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public SessionBean sessionBean() {
return new SessionBean();
}
}
#Component
public class InitializeUserLanguageHandler extends AbstractHandlerInterceptor {
#Autowired
private SessionBean sessionBean;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (sessionBean.getLanguage() == null) {
sessionBean.setLanguage(getUserLanguage());
}
return true;
}
}
The test part:
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringMvcConfiguration.class)
public class BaseSpringMvcIntegrationTest {
#Resource
protected WebApplicationContext webApplicationContext;
protected MockMvc mockMvc;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
}
public class InitializeUserLanguageHandlerTest extends BaseSpringMvcIntegrationTest {
#Autowired
private SessionBean sessionBean;
#Autowired
private MockHttpSession mockHttpSession;
#Test
public void testLanguageIsInitializedOnlyOnce() throws Exception {
MockHttpSession mocksession = new MockHttpSession();
// It is null, this is because the interceptor has not been called yet
assertEquals(null, sessionBean.getLanguage());
// This line will call the interceptor and set language to "nl"
mockMvc.perform(get("/").session(mocksession).principal(getUser("nl")));
// It is null, but I expect it to be "nl"
assertEquals(null, sessionBean.getLanguage());
// Let's try again
mockMvc.perform(get("/").session(mocksession).principal(getUser("fr")));
// It is null, but I expect it to be "nl"
assertEquals(null, sessionBean.getLanguage());
}
}
You can see in the test class "InitializeUserLanguageHandlerTest" that I have some assertions.
The first time I call:
mockMvc.perform(get("/").session(mocksession).principal(getUser()));
The code in the interceptor is executed and language is set to "nl". Therefore, in my test, I would have expected that sessionBean.getLanguage() would return me "nl", but it is not. I don't understand why.
So I'm calling the perform again, the interceptor code is executed again, and calling sessionBean.getLanguage() returns "nl".
It seems I've two SessionBean instances, one in my test and the other in the source. But when I look at the SessionBean variable in Eclipse in Debug mode, they have the same ID.
If I change the "session" scope to "application" scope, it is working properly.
Can somebody help me ?
Thank you.
Here is one way to solve the problem, not sure it is the best though.
#ContextConfiguration(classes = {SpringMvcConfiguration.class, InitializeUserLanguageHandlerTest.BeanConfig.class})
public class InitializeUserLanguageHandlerTest extends BaseSpringMvcIntegrationTest {
#Configuration
public static class BeanConfig {
#Bean(name = "sessionBean")
public SessionBean sessionBean() {
return new SessionBean();
}
}
...
}
I want to test a class using Spring + JUnit + Mockito but I don't manage to make it work properly.
Let's say my class references a Service:
#Controller
public class MyController
{
#Autowired
private MyService service;
#PostConstruct
public void init() {
service.whatever();
}
public void doSomething() {
service.create();
}
}
And this Service references a Repository:
#Service
public class MyService {
#Autowired
private MyRepository repository;
public void whatever() {}
public void create() {
repository.save();
}
}
When testing the MyController class, I want the service to be mocked. The problem is: even when the service is mocked, Spring tries to inject the repository in the mock.
Here is what I did. Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { MyControllerTestConfiguration.class })
public class MyControllerTest {
#Autowired
private MyController myController;
#Test
public void testDoSomething() {
myController.doSomething();
}
}
Configuration class:
#Configuration
public class MyControllerTestConfiguration {
#Bean
public MyController myController() {
return new MyController();
}
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
}
And the error I get: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [test.test.MyRepository] found for dependency
I tried to initialize the mock using Mockito's #InjectMocks annotation but this fails because the #PostConstruct method is called before the mocks injection, generating a NullPointerException.
And I cannot simply mock the repository because in real life that would make me mock A LOT of classes...
Can anyone help me on this?
Use constructor instead of field injection. That makes testing a lot easier.
#Service
public class MyService {
private final MyRepository repository;
#Autowired
public MyService(MyRepository repository) {
this.repository = repository;
}
public void whatever() {}
public void create() {
repository.save();
}
}
-
#Controller
public class MyController {
private final MyService service;
#Autowired
public MyController(MyService service) {
this.service = service;
}
#PostConstruct
public void init() {
service.whatever();
}
public void doSomething() {
service.create();
}
}
This has several advantages:
You don't need Spring in your tests. This allows you to do proper unit tests. It also makes the test incredibly fast (from seconds to milliseconds).
You cannot accidentally create an instance of a class without its dependencies which would result in a NullPointerException.
As #NamshubWriter pointed out:
[The instance fields for the dependencies] can be final, so 1) they cannot be accidentally modified, and 2) any thread reading the field will read the same value.
Discard the #Configuration class and write a test like this:
#RunWith(MockitoJUnitRunner.class)
public class MyControllerTest {
#Mock
private MyRepository repository;
#InjectMocks
private MyService service;
#Test
public void testDoSomething() {
MyController myController = new MyController(service);
myController.doSomething();
}
}
Use interfaces, especially if you use some kind of AOP (transactions, security, etc), i.e. you'll have interface MyService and class MyServiceImpl.
In configuration you'll have:
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
you should put the #InjectMocks annotation in your controller and #Mock in your service, look:
#Autowired
#InjectMocks
private MyController myController;
#Autowired
#Mock
private MyService myService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testDoSomething() {
myController.doSomething();
}
I am trying to write a IntegrationFlow test. It goes something like this:
JMS(in) -> (find previous versions in db) -> reduce(in,1...n) -> (to db) -> JMS(out)
So, no suprise: I want to mock the DB calls; they are Dao beans. But, I also want it to pickup other beans through component scan; I will selectively scan all packages except dao.
Create a test config and mock the Daos. No problem
Follow spring boot instructions for testing to get Component scanned beans. No problem
I just want to verify the sequence of steps and the resultant output as the outbound JMS queue would see it. Can someone just help me fill in the blanks?
This CANT be tough! The use of mocks seems to be problematic because plenty of essential fields are final. I am reading everywhere about this and just not coming up with a clear path. I inherited this code BTW
My error:
org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers
Here is my code
#Configuration
#ImportResource("classpath:retry-context.xml")
public class LifecycleConfig {
#Autowired
private MessageProducerSupport inbound;
#Autowired
private MessageHandler outbound;
#Autowired
#Qualifier("reducer")
private GenericTransformer<Collection<ExtendedClaim>,ExtendedClaim> reducer;
#Autowired
#Qualifier("claimIdToPojo")
private GenericTransformer<String,ClaimDomain> toPojo;
#Autowired
#Qualifier("findPreviousVersion")
private GenericTransformer<ExtendedClaim,Collection<ExtendedClaim>> previousVersions;
#Autowired
#Qualifier("saveToDb")
private GenericHandler<ExtendedClaim> toDb;
#Bean
public DirectChannel getChannel() {
return new DirectChannel();
}
#Bean
#Autowired
public StandardIntegrationFlow processClaim() {
return IntegrationFlows.from(inbound).
channel(getChannel()).
transform(previousVersions).
transform(reducer).
handle(ExtendedClaim.class,toDb).
transform(toPojo).
handle(outbound).get();
}
}
Test Config
#Configuration
public class TestConfig extends AbstractClsTest {
#Bean(name = "claimIdToPojo")
public ClaimIdToPojo getClaimIdToPojo() {
return spy(new ClaimIdToPojo());
}
#Bean
public ClaimToId getClaimToIdPojo() {
return spy(new ClaimToId());
}
#Bean(name = "findPreviousVersion")
public FindPreviousVersion getFindPreviousVersion() {
return spy(new FindPreviousVersion());
}
#Bean(name = "reducer")
public Reducer getReducer() {
return spy(new Reducer());
}
#Bean(name = "saveToDb")
public SaveToDb getSaveToDb() {
return spy(new SaveToDb());
}
#Bean
public MessageProducerSupport getInbound() {
MessageProducerSupport mock = mock(MessageProducerSupport.class);
// when(mock.isRunning()).thenReturn(true);
return mock;
}
#Bean
public PaymentDAO getPaymentDao() {
return mock(PaymentDAO.class);
}
#Bean
public ClaimDAO getClaimDao() {
return mock(ClaimDAO.class);
}
#Bean
public MessageHandler getOutbound() {
return new CaptureHandler<ExtendedClaim>();
}
}
Actual test won't load
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestConfig.class, LifecycleConfig.class})
public class ClaimLifecycleApplicationTest extends AbstractClsTest {
#Autowired
private MessageHandler outbound;
#Autowired
#Qualifier("reducer")
private GenericTransformer<Collection<ExtendedClaim>,ExtendedClaim> reducer;
#Autowired
#Qualifier("claimIdToPojo")
private GenericTransformer<String,ClaimDomain> toPojo;
#Autowired
#Qualifier("findPreviousVersion")
private GenericTransformer<ExtendedClaim,Collection<ExtendedClaim>> previousVersions;
#Autowired
#Qualifier("saveToDb")
private GenericHandler<ExtendedClaim> toDb;
#Autowired
private DirectChannel defaultChannel;
#Test
public void testFlow() throws Exception {
ExtendedClaim claim = getClaim();
Message<ExtendedClaim> message = MessageBuilder.withPayload(claim).build();
List<ExtendedClaim> previousClaims = Arrays.asList(claim);
defaultChannel.send(message);
verify(previousVersions).transform(claim);
verify(reducer).transform(previousClaims);
verify(toDb).handle(claim, anyMap());
verify(toPojo).transform(claim.getSubmitterClaimId());
verify(outbound);
}
}
There are a lot of domain-specific object, so I can't test it to reproduce or find some other issue with your code.
But I see that you don't use an #EnableIntegration on your #Configurations classes.