I am new to AOP and currently trying to implement an aspect for controller methods annotated with a custom annotation.
I allways get a NullPointerException on repository when myFunction is invoked. It seems that i have two instances of of the controller. One of them is instantiated without autowiring the repository. When i remove #MyAnnotation everything works as expected.
Can you give me any hint on how to force Spring/AspectJ to use my constructor?
My controller looks like:
#RestController
#RequestMapping(value = "/api")
public class MyController {
private Repository repository;
public MyController(Repository repository) {
this.repository = repository;
}
#RequestMapping(value = "/{variable1}", method = GET)
#MyAnnotation
public final FeatureCollection myFunction(
final #PathVariable(required = true) long variable1
) {
repository.findById(variable1);
(...)
}
}
The aspect:
#Aspect
public class MyAspect {
#Around("#annotation(MyAnnotation)")
public Object any(ProceedingJoinPoint pjp) throws Throwable {
return pjp.proceed();
}
}
The configuration:
#Configuration
#EnableAspectJAutoProxy
public class WebConfig {
#Bean
public MyAspect myAspect() {
return new MyAspect();
}
}
Try to Annotate Constructor with #Autowired
#Autowired
public MyController(Repository repository) {
this.repository = repository;
}
It comes from the fact that the annotated method is final.
If you can remove the final keyword, it'll work.
If you google something like "spring aop final methods", you'll find more info, but basically, when Spring creates the proxy, it generates a subclass from your original class, to wrap the call to super with your aspect. The problem is that a final method cannot be inherited by the subclass, so that makes it hard for the proxy to work. Therefore, a lot of limitations come from that, and AOP doesn't work that much with final stuff.
This limitation is mentioned in the docs, I don't think you'll get a workaround for it:
11.6 Proxying mechanisms
...
final methods cannot be advised, as they cannot be overridden.
Hoping this will help you!
Related
This is the setup of the test class:
#RunWith(PowerMockRunner.class)
#PowerMockIgnore("javax.management.*")
#PowerMockRunnerDelegate(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = ServiceApplication.class)
#PrepareForTest({ MyClass.class })
public class ControllerTest {
#Autowired
public TestRestTemplate restTemplate;
public static MyClass myClass = Mockito.mock(MyClass.class);
#BeforeClass
public static void beforeClassSetup() throws Exception {
PowerMockito.mockStatic(MyClass.class);
BDDMockito.given(MyClass.getInstance(Mockito.anyString())).willReturn(myClass);
BDDMockito.given(myClass.foo()).willReturn("BAR");
// ...
}
.
.
.
// test cases
In configuration class of this project, for load some beans, I use this static call for generate the instance.
#Configuration
#ComponentScan(basePackages = { "package.from.another.project.in.production" })
public class Beans {
#Bean
public MyClass myClass() {
return MyClass.getInstance(K.FOO);
}
}
This is my controller that uses the bean, as well as the static call according to the parameters.
#RestController
public class Controller {
#Autowired
private MyClass myClass;
#GetMapping(path = "/")
public String doSomething() {
String filter = myClass.foo();
return filter;
}
#GetMapping(path = "/two")
public String doSomething2(#RequestParam Map<String, String> allParams) {
String accountId = allParams.get("account_id");
String filter = MyClass.getInstance(K.BAR + accountId).foo();
return filter;
}
}
The bean is autowired because its use is greater than instantiation by the getInstance() method. In addition, the instantiation by the getIntance() method is variable according to the parameter. Don't ask me why the MyClass class is like this, because the API was old and I'm slowly refactoring.
The issue is that the autowired bean is correctly mocked by PowerMockito.mockStatic(MyClass.class) and also by #MockBean (which I used initially), but the call MyClass.getInstance() in Controller.class does not work at all.
I think the problem should happen when Spring climbs its environment and does not load everything that has been correctly mocked by PowerMockito, just the classes of its beans. Can anyone help me solve this problem?
This is just a wild guess, have you tried using regular Mockito as opposed to BDDMockito? Just want to rule it out as a culprit.
I am trying to use #Async annotation provided by spring. Going through some of the blogs I found there are the following constraints for using it:
It must be applied to public methods only
Self-invocation – calling the async method from within the same class – won’t work
I have a method which is getting called from the same class which I want to mark #Async. Is there any way of achieving it from the same class?
In Spring v4.3+ you can use self injection, and call the method on the self injected reference.
So for example:
#Component
public class SomeClass {
#Autowired
private SomeClass selfInjected;
public void someMethod() {
selfInjected.someOtherMethod();
}
#Async
public void someOtherMethod(){
...;
}
}
Updated as OP is using version before 4.3:
This will work for you.
#Component
public class SomeClass {
#Autowired
private ApplicationContext applicationContext;
private SomeClass selfInjected;
#PostConstruct
private void init() {
selfInjected = applicationContext.getBean(SomeClass.class);
}
}
Or
The other option is to extract the method to separate class and autowire it. I would personally explore this option before doing the above method.
This question is intended to make an answer for a useful issue.
Suppose we have a Spring application with a #Controller, an interface and different implementations of that interface.
We want that the #Controller use the interface with the proper implementation, based on the request that we receive.
Here is the #Controller:
#Controller
public class SampleController {
#RequestMapping(path = "/path/{service}", method = RequestMethod.GET)
public void method(#PathVariable("service") String service){
// here we have to use the right implementation of the interface
}
}
Here is the interface:
public interface SampleInterface {
public void sampleMethod(); // a sample method
}
Here is one of the possibile implementation:
public class SampleInterfaceImpl implements SampleInterface {
public void sampleMethod() {
// ...
}
}
And here is another one:
Here is one of the possibile implementation:
public class SampleInterfaceOtherImpl implements SampleInterface {
public void sampleMethod() {
// ...
}
}
Below I'll show the solution that I've found to use one of the implementations dynamically based on the request.
The solution I've found is this one.
First, we have to autowire the ApplicationContext in the #Controller.
#Autowired
private ApplicationContext appContext;
Second, we have to use the #Service annotation in the implementations of the interface.
In the example, I give them the names "Basic" and "Other".
#Service("Basic")
public class SampleInterfaceImpl implements SampleInterface {
public void sampleMethod() {
// ...
}
}
#Service("Other")
public class SampleInterfaceOtherImpl implements SampleInterface {
public void sampleMethod() {
// ...
}
}
Next, we have to obtain the implementation in the #Controller.
Here's one possible way:
#Controller
public class SampleController {
#Autowired
private ApplicationContext appContext;
#RequestMapping(path = "/path/{service}", method = RequestMethod.GET)
public void method(#PathVariable("service") String service){
SampleInterface sample = appContext.getBean(service, SampleInterface.class);
sample.sampleMethod();
}
}
In this way, Spring injects the right bean in a dynamic context, so the interface is resolved with the properly inmplementation.
I solved that problem like this:
Let the interface implement a method supports(...) and inject a List<SampleInterface> into your controller.
create a method getCurrentImpl(...) in the controller to resolve it with the help of supports
since Spring 4 the autowired list will be ordered if you implement the Ordered interface or use the annotation #Order.
This way you have no need for using the ApplicationContext explicitly.
Honestly I don't think the idea of exposing internal implementation details in the URL just to avoid writing some lines of code is good.
The solution proposed by #kriger at least adds one indirection step using a key / value approach.
I would prefer to create a Factory Bean (to be even more enterprise oriented even an Abstract Factory Pattern) that will choose which concrete implementation to use.
In this way you will be able to choose the interface in a separate place (the factory method) using any custom logic you wish.
And you will be able to decouple the service URL from the concrete implementation (which is not very safe).
If you are creating a very simple service your solution will work, but in an enterprise environment the use of patterns is vital to ensure maintenability and scalability.
I'm not convinced with your solution because there's an implicit link between an HTTP parameter value and a bean qualifier. Innocent change of the bean name would result in a disaster that could be tricky to debug. I would encapsulate all the necessary information in one place to ensure any changes only need to be done in a single bean:
#Controller
public class SampleController {
#Autowired
private SampleInterfaceImpl basic;
#Autowired
private SampleInterfaceOtherImpl other;
Map<String, SampleInterface> services;
#PostConstruct
void init() {
services = new HashMap()<>;
services.put("Basic", basic);
services.put("Other", other);
}
#RequestMapping(path = "/path/{service}", method = RequestMethod.GET)
public void method(#PathVariable("service") String service){
SampleInterface sample = services.get(service);
// remember to handle the case where there's no corresponding service
sample.sampleMethod();
}
}
Also, dependency on the ApplicationContext object will make it more complicated to test.
NB. to make it more robust I'd use enums instead of the "Basic" and "Other" strings.
However, if you know you'll only have two types of the service to choose from, this would be the "keep it simple stupid" way:
#Controller
public class SampleController {
#Autowired
private SampleInterfaceImpl basic;
#Autowired
private SampleInterfaceOtherImpl other;
#RequestMapping(path = "/path/Basic", method = RequestMethod.GET)
public void basic() {
basic.sampleMethod();
}
#RequestMapping(path = "/path/Other", method = RequestMethod.GET)
public void other() {
other.sampleMethod();
}
}
I want configure a component test using spring-test configuration inner class (#Configuration). Tested components has some services which I'd like to mock for the test. These services are classes (no interface used) and have spring annotations (#Autowired) in them. Mockito can easily mock them, however, I found no way of disabling spring autowiring.
Example how I can easily reproduce:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
#Autowired
private TestedBean entryPoint;
#Test
public void test() {
}
#Configuration
#ImportResource("/spring/component-config.xml")
static class Beans {
#Bean
ThirdPartyService createThirdPartyService() {
return mock(ThirdPartyService.class);
}
}
}
public class ThirdPartyService {
#Autowired
Foo bar;
}
public class TestedBean {
#Autowired
private ThirdPartyService service;
}
In this example "TestBean" represents the service to be mocked. I would NOT like "bar" to be injected by spring! #Bean(autowire = NO) does not help (in fact, that's the default value).
(Please save me from "use interfaces!" comments - the mocked service can be 3rd party which I can't do anything with.)
UPDATE
Springockito partially solves the problem, as long as you don't have to have anything else to configure (so you can't use configuration class with Springockito - it does not support it), but use mocks only.
Still looking for pure spring solution, if there's any...
Here is my solution to your problem:
import static org.mockito.Mockito.mockingDetails;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public 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();
}
}
}
and then just
#Import(MockitoSkipAutowireConfiguration.class)
in your test #Configuration and you are all set
I solved it by creating FactoryBean for my bean instead of just mocking bean. At this way Spring don't try to autowire fields.
Factory bean helping class:
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private final Class<T> clazz;
public MockitoFactoryBean(Class<T> clazz) {
this.clazz = clazz;
}
#Override public T getObject() throws Exception {
return mock(clazz);
}
#Override public Class<T> getObjectType() {
return clazz;
}
#Override public boolean isSingleton() {
return true;
}
}
Actual test context part:
#Configuration
public class TestContext {
#Bean
public FactoryBean<MockingService> mockingService() {
return new MockitoFactoryBean<>(MockingService.class);
}
}
Check Spring profiles. You don't need to disable auto wiring, you need to inject different beans for different configuration.
You could add the mocked service manually to the spring application context via org.springframework.beans.factory.config.SingletonBeanRegistry#registerSingleton. This way the mock is not post-processed by spring and spring does not attempt to autowire the mock. The mock itself will be injected into your tested bean.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
#Autowired
private TestedBean entryPoint;
#Autowired
private ThirdPartyService thirdPartyServiceMock;
#Test
public void test() {
}
#Configuration
static class Beans {
#Autowired
private GenericApplicationContext ctx;
#Bean
TestedBean testedBean() {
ctx.getBeanFactory().registerSingleton("thirdPartyService", mock(ThirdPartyService.class));
return new TestedBean();
}
}
public static class ThirdPartyService {
#Autowired
Object bar;
}
public static class TestedBean {
#Autowired
private ThirdPartyService service;
}
}
I am in quite the same situation.
What I found that if you do not set the context loader by #ContextConfiguration annotation on your test class, the default context loader will be used, which derived from AbstractGenericContextLoader. I had a look at its source and turned out it registers all the bean post processors which are responsible for reading annotations such #Autowired. In other words, annotation config is enabled by default.
So the main problem is that there are two configurations which are in conflict: in the java config we said that autowiring is not needed, while the autowired annotation tells the opposite. The real question is how to disable the annotation processing in order to eliminate the undesired configuration.
As far as I know there is no such spring implementation of ContextLoader which would not be derived from AbstractGenericContextLoader so I guess the only we can do is to write our own. It would be something like this:
public static class SimpleContextLoader implements ContextLoader {
#Override
public String[] processLocations(Class<?> type, String... locations) {
return strings;
}
#Override
public ApplicationContext loadContext(String... locations) throws Exception {
// in case of xml configuration
return new ClassPathXmlApplicationContext(strings);
// in case of java configuration (but its name is quite misleading)
// return new AnnotationConfigApplicationContext(TestConfig.class);
}
}
Of course it would be worth to spend more time to find out how to implement ContextLoader properly.
Cheers,
Robert
There are so many ways of doing this, I'm pretty sure that this answer will be incomplete, but here are a few options...
As currently seems to be recommended practice, use constructor injection for your services rather than autowiring the fields directly. This makes testing like this so much easier.
public class SomeTest {
#Mock
private ThirdPartyService mockedBean;
#Before
public void init() {
initMocks(this);
}
#Test
public void test() {
BeanUnderTest bean = new BeanUnderTest(mockedBean);
// ...
}
}
public class BeanUnderTest{
private ThirdPartyService service;
#Autowired
public BeanUnderTest(ThirdPartyService ThirdPartyService) {
this.thirdPartyService = thirdPartyService;
}
}
By doing that, you can also mix up autowired and mocked services by autowiring into the test itself and then constructing the beans under test with the most useful mix of autowired and mocked beans.
A reasonable alternative is to use Spring profiles to define stub services. This is particularly useful when wish to use the same stubbed features in multiple tests:
#Service
#Primary
#Profile("test")
public class MyServiceStub implements MyService {
// ...
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
#ActiveProfiles({"test"})
public class SomeTest {
// ...
}
By using the #Primary annotation, it ensures that this stub bean will be used instead of any other bean implementing the MyService interface. I tend to use this approach for things like email services, where by changing profile, I'm able to switch between a real mail server and Wiser.
I have a component setup that is essentially a launcher for an application. It is configured like so:
#Component
public class MyLauncher {
#Autowired
MyService myService;
//other methods
}
MyService is annotated with the #Service Spring annotation and is autowired into my launcher class without any issues.
I would like to write some jUnit test cases for MyLauncher, to do so I started a class like this:
public class MyLauncherTest
private MyLauncher myLauncher = new MyLauncher();
#Test
public void someTest() {
}
}
Can I create a Mock object for MyService and inject it into myLauncher in my test class? I currently don't have a getter or setter in myLauncher as Spring is handling the autowiring. If possible, I'd like to not have to add getters and setters. Can I tell the test case to inject a mock object into the autowired variable using an #Before init method?
If I'm going about this completely wrong, feel free to say that. I'm still new to this. My main goal is to just have some Java code or annotation that puts a mock object in that #Autowired variable without me having to write a setter method or having to use an applicationContext-test.xml file. I would much rather maintain everything for the test cases in the .java file instead of having to maintain a separate application content just for my tests.
I am hoping to use Mockito for the mock objects. In the past I have done this by using org.mockito.Mockito and creating my objects with Mockito.mock(MyClass.class).
You can absolutely inject mocks on MyLauncher in your test. I am sure if you show what mocking framework you are using someone would be quick to provide an answer. With mockito I would look into using #RunWith(MockitoJUnitRunner.class) and using annotations for myLauncher. It would look something like what is below.
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
#InjectMocks
private MyLauncher myLauncher = new MyLauncher();
#Mock
private MyService myService;
#Test
public void someTest() {
}
}
The accepted answer (use MockitoJUnitRunner and #InjectMocks) is great. But if you want something a little more lightweight (no special JUnit runner), and less "magical" (more transparent) especially for occasional use, you could just set the private fields directly using introspection.
If you use Spring, you already have a utility class for this : org.springframework.test.util.ReflectionTestUtils
The use is quite straightforward :
ReflectionTestUtils.setField(myLauncher, "myService", myService);
The first argument is your target bean, the second is the name of the (usually private) field, and the last is the value to inject.
If you don't use Spring, it is quite trivial to implement such a utility method. Here is the code I used before I found this Spring class :
public static void setPrivateField(Object target, String fieldName, Object value){
try{
Field privateField = target.getClass().getDeclaredField(fieldName);
privateField.setAccessible(true);
privateField.set(target, value);
}catch(Exception e){
throw new RuntimeException(e);
}
}
Sometimes you can refactor your #Component to use constructor or setter based injection to setup your testcase (you can and still rely on #Autowired). Now, you can create your test entirely without a mocking framework by implementing test stubs instead (e.g. Martin Fowler's MailServiceStub):
#Component
public class MyLauncher {
private MyService myService;
#Autowired
MyLauncher(MyService myService) {
this.myService = myService;
}
// other methods
}
public class MyServiceStub implements MyService {
// ...
}
public class MyLauncherTest
private MyLauncher myLauncher;
private MyServiceStub myServiceStub;
#Before
public void setUp() {
myServiceStub = new MyServiceStub();
myLauncher = new MyLauncher(myServiceStub);
}
#Test
public void someTest() {
}
}
This technique especially useful if the test and the class under test is located in the same package because then you can use the default, package-private access modifier to prevent other classes from accessing it. Note that you can still have your production code in src/main/java but your tests in src/main/test directories.
If you like Mockito then you will appreciate the MockitoJUnitRunner. It allows you to do "magic" things like #Manuel showed you:
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
#InjectMocks
private MyLauncher myLauncher; // no need to call the constructor
#Mock
private MyService myService;
#Test
public void someTest() {
}
}
Alternatively, you can use the default JUnit runner and call the MockitoAnnotations.initMocks() in a setUp() method to let Mockito initialize the annotated values. You can find more information in the javadoc of #InjectMocks and in a blog post that I have written.
I believe in order to have auto-wiring work on your MyLauncher class (for myService), you will need to let Spring initialize it instead of calling the constructor, by auto-wiring myLauncher. Once that is being auto-wired (and myService is also getting auto-wired), Spring (1.4.0 and up) provides a #MockBean annotation you can put in your test. This will replace a matching single beans in context with a mock of that type. You can then further define what mocking you want, in a #Before method.
public class MyLauncherTest
#MockBean
private MyService myService;
#Autowired
private MyLauncher myLauncher;
#Before
private void setupMockBean() {
doNothing().when(myService).someVoidMethod();
doReturn("Some Value").when(myService).someStringMethod();
}
#Test
public void someTest() {
myLauncher.doSomething();
}
}
Your MyLauncher class can then remain unmodified, and your MyService bean will be a mock whose methods return values as you defined:
#Component
public class MyLauncher {
#Autowired
MyService myService;
public void doSomething() {
myService.someVoidMethod();
myService.someMethodThatCallsSomeStringMethod();
}
//other methods
}
A couple advantages of this over other methods mentioned is that:
You don't need to manually inject myService.
You don't need use the Mockito runner or rules.
I'm a new user for Spring. I found a different solution for this. Using reflection and making public necessary fields and assign mock objects.
This is my auth controller and it has some Autowired private properties.
#RestController
public class AuthController {
#Autowired
private UsersDAOInterface usersDao;
#Autowired
private TokensDAOInterface tokensDao;
#RequestMapping(path = "/auth/getToken", method = RequestMethod.POST)
public #ResponseBody Object getToken(#RequestParam String username,
#RequestParam String password) {
User user = usersDao.getLoginUser(username, password);
if (user == null)
return new ErrorResult("Kullanıcıadı veya şifre hatalı");
Token token = new Token();
token.setTokenId("aergaerg");
token.setUserId(1);
token.setInsertDatetime(new Date());
return token;
}
}
And this is my Junit test for AuthController. I'm making public needed private properties and assign mock objects to them and rock :)
public class AuthControllerTest {
#Test
public void getToken() {
try {
UsersDAO mockUsersDao = mock(UsersDAO.class);
TokensDAO mockTokensDao = mock(TokensDAO.class);
User dummyUser = new User();
dummyUser.setId(10);
dummyUser.setUsername("nixarsoft");
dummyUser.setTopId(0);
when(mockUsersDao.getLoginUser(Matchers.anyString(), Matchers.anyString())) //
.thenReturn(dummyUser);
AuthController ctrl = new AuthController();
Field usersDaoField = ctrl.getClass().getDeclaredField("usersDao");
usersDaoField.setAccessible(true);
usersDaoField.set(ctrl, mockUsersDao);
Field tokensDaoField = ctrl.getClass().getDeclaredField("tokensDao");
tokensDaoField.setAccessible(true);
tokensDaoField.set(ctrl, mockTokensDao);
Token t = (Token) ctrl.getToken("test", "aergaeg");
Assert.assertNotNull(t);
} catch (Exception ex) {
System.out.println(ex);
}
}
}
I don't know advantages and disadvantages for this way but this is working. This technic has a little bit more code but these codes can be seperated by different methods etc. There are more good answers for this question but I want to point to different solution. Sorry for my bad english. Have a good java to everybody :)
Look at this link
Then write your test case as
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"/applicationContext.xml"})
public class MyLauncherTest{
#Resource
private MyLauncher myLauncher ;
#Test
public void someTest() {
//test code
}
}