How should I test a method with an aspect around? - spring

I'm trying to do an integration test. These are my components:
UsersService
#ManageSession
public List<T> get() {
//...
return something;
}
ManageSessionAspect
#Autowired
AuthService authService;
#Before("manageSessionMethod()") //methods annotated with #ManageSession
public void doSomething() {
authService.doSomething();
}
I need to test UsersService.get() method. But I want to disable the aspect OR I want to be able to mock the AuthService inside it.
I have tried using an is() pointcut, but I get:
if() pointcut designator isn't supported by Spring.
Can you help me? Thanks.

This is a perfect use case for spring profiles. You can define profiles and tie your configuration classes to these profiles. A configuration tied to a profile will only get activated if the profile is active. See my answer on a related question for further details: A: How to mock bean and avoiding NoUniqueBeanDefinitionException. You will need to define a profile (eg.: test or integration-test) and use that profile in a configuration class to provide a mock implementation for your AuthService.
As a side note, I would strongly suggest you go with AspectJ (preferably compile-time weaving) instead of Spring AOP as it's much more powerful.

Related

how to correctly modularize app configuration, so that tests(IT,datajpa,...) does not pick up everything intended for production

In our app I found out, that my integration tests picks up more stuff than I'd like. I'd like to know, how correctly structured app configuration looks like, what do you use, so that I can #Import in tests only those configuration which are used in production, which are needed.
I believe relevant page in documentation is:
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-user-configuration
... it's stressed there, that it's important to structure code in sensible way, however it's not shown that much, what that is/means. I know about profiles and can probably create profile which would be unmatched in tests and import manually, but that's probably not that sensible way they were talking about.
Consider this main entrypoint:
#SpringBootApplication
public class DemoApplication {
private final SomeService someService;
public DemoApplication(SomeService someService) {
this.someService = someService;
}
#EventListener(ApplicationReadyEvent.class)
public void started() {
System.out.println(someService.doIt());
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
interface of some service:
public interface SomeService {
public String doIt();
}
and configuration:
#Configuration
public class Config {
#Bean
public SomeService createSomeServiceBean() {
return new SomeService() {
#Override
public String doIt() {
return String.format("Hi! (At %s)", LocalDateTime.now());
}
};
}
}
When invoked, entrypoint annotated by #SpringBootApplication will do component scan, will discover configuration and it will work. Reading further in documentation we will find sentence: Test slices exclude #Configuration classes from scanning([if #ComponentScan does have default value of basePackages and basePackagesClasses]), however following test:
#SpringBootTest
class DemoApplicationTests {
#Autowired
private SomeService someService;
#Test
void contextLoads() {
System.out.println(someService.doIt());
}
}
just happily discovers SomeService bean defined. Or did that sentence meant just that tests annotated by for example #DataJpaTest won't register some configurations? Kinda unclear to me, but it does not seem possible, since how would #DataJpaTest would know, which configurations to ommit and which not.
Again, I know how to use profiles/excluding configurations. I'm asking about "sensible way of structuring app".
How to sensibly structure you app and how to configure it so that:
#SpringBootApplication annotated entrypoint will do component scan, find and use configurations, for production, but these configurations needs to be manually imported in tests?
some packages will be automatically scanned for configurations which will be used both in development and tests environments.
The Spring Boot Test support provides annotations that allow to only create a Spring Context with the relevant beans to testing a specific slice of your application.
There is no specific package structure or naming strategy required to make use of this feature.
Here are some of these:
#DataJpaTest: You get a Spring Context with relevant beans to test your JPA <-> Database interface: EntityManager, DataSource, all your interfaces extending JpaRepository
#WebMvcTest: You get a Spring Context with a mocked servlet environment for testing your web layer that includes the following beans for your: all your controller, controller advice, WebMvcConfigurer , Filter, etc. but not anything that is annotated with e.g. #Service or #Component
#SpringBootTest: This will give you a full Spring Context and tries to create all beans for you. You can exclude some autoconfiguration e.g. if you don't want autoconfiguration to kick in:
Example:
#SpringBootTest(webEnvironment = RANDOM_PORT)
#TestPropertySource(properties=
{"spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration"})
There are way more test slice annotations, you can have a look at here
So these annotations are smart in a way that they know which beans they should include in the context and which to exclude.
A general approach to testing your application can be to use the first two test annotations stated above to verify web and data layer in isolation. Next use Mockito and plain JUnit 5 to unit test your service classes. And finally, write some integration test that creates the whole Spring Context with #SpringBootTest to test everything together.

Spock with Spring Boot and Camel: Zero interactions with detached mock

I am having some issues with testing my camel context with spring boot.
I am using spring boot 1.5.6, spock 1.1-groovy-2.4, camel 2.19.2, and camel-spring-boot-starter 2.19.2.
I am using a spock mock, and I'm using the DetachedMockFactory in a #TestConfiguration class. All of my beans use constructor injection. I am injecting a mocked #Repository into one of the processor #Components, and I am also injecting it into my test class to define interactions.
I have my test annotated with #SpringBootTest with the classes list including all Processor implementations, and all RouteBuilder extensions. I also have an '#Import' with my TestConfiguration class. I am even using constructor injection for this repository bean in my test!
But it seems that the mock that is injected into the test class is not the one that is in use. Does anyone have an idea what could be wrong? I have tried #DirtiesContext to reload the context both before and after each test, but that did not help.
Problems with DetachedMocks not behaving correctly, e.g., appearing to be the same instance, are usually caused by some framework wrapping them in proxies. For example this can be caused by #Transactional annotation in Spring, which creates a proxy to facilitate jdbc-session management. See also issue #758
For spring you can use the methods of AopUtils (jdoc). The simple way is to use AopUtils.isAopProxy to check if it is proxied by spring an then unwrap it.
public static <T> T getTargetObject(Object proxy) throws Exception {
if (AopUtils.isAopProxy(proxy)) {
return (T) ((Advised) proxy).getTargetSource().getTarget();
} else {
return (T) proxy;
}
}
And in a Test
def "sample service test"() {
given:
def sampleRepositryMock = getTargetObject(sampleRepositry)
when:
sampleService.doSomething() // simply invoke sampleRepositry.doSomething() in it
then:
1 * sampleRepositryMock.doSomething()
0 * _
}
Edit: Since Spock 1.2 there is an extension to automatically unwrap injected beans #UnwrapAopProxy.
#Inject
#UnwrapAopProxy
SampleRepositry sampleRepositryMock
If someone comes up with the same problem.
Spock added additional #UnwrapAopProxy that will do the job for you instead of the util method mentioned above. You can also drop the DetachedMockFactory
#SpringSpy
#UnwrapAopProxy
Service service

Dynamic AOP configuration in Spring

I'm new to Spring AOP, and I've to know if it's possible to implement my project with Spring AOP. Specifically, I want to know if in Spring AOP it's possible to configure, at runtime, which aspects will be executed and in which order. NB: I want to control only mine application aspects', without interfering with Spring-managed aspects like transactions or security.
You can control the ordering of aspesct (using declare precedence), but its hard to do this at run time, as it would need to reapply the weaving for already weaved classes.
The same goes for specifying which aspects to execute.
You could have an if(!enabled){return;} check at the start of each aspect method, and the enabled flag could be set on or off just like any normal bean. This way, the aspects are always weaved in, but they will do nothing if they are disabled.
UPDATE
Here is example of how to use an aspect as a bean.
Lets say you have an aspect like this:
#Component // IMPORTANT
public aspect MyAspect {
Object responder;
public void setResponder(Object object) {
this.responder=object;
}
}
Then in your spring service class, you can wiring the aspect like this:
#Service
public class MyServiceImpl implements MyService {
#Autowired
MyAspect aspect;
void action() {
aspect.setResponder(null);
}
}
This works because aspects are actually java classes. The component annotation causes the aspect to be recognised as a bean by the spring component scan.
I have compiled this code successfully, but not actually tested it runtime, so would be good to know if it works.

Logging with spring-aop

I am trying to add some kind of tracing to my app. It would work like that: I set package name in my config and all calls to public methods of classes in this package are logged.
I can't use weaving here (due to some technical reasons), so I tried to use spring-proxy.
My pointcut
execution(public * com.my.package.*.*(..))
and it works fine. But what Spring uses Proxy or CGLIB to proxy class, so each class should at least
Have parameterless constructor
All methods should be implementations of some interface
Other classes could not be proxied. That is ok for me to do not log them. But spring creates proxies and ruins my app.
How can I tell spring: "do not proxy this class if it has no parameterless constructor and some of its methods are not implementations of interface"?
Not sure about the answer to your question, however, another way to do the tracing, is to use an annotation:
public #interface Monitor {
}
aspect:
public aspect MonitoringAspect {
pointcut methodToMonitor() : execution(#Monitor * *(..));
}
Usage:
#Monitor
public void methodToBeMonitored() {
}
This way you only affect the methods you want to.
I suppose for a big project, this might not work as you want to monitor many methods.

How to use spring to resolve dependencies of an object created manually?

I would like to know if it's possible to use Spring to resolve the dependencies of an object created manually in my program. Take a look at the following class:
public class TestClass {
private MyDependency md;
public TestClass() {
}
...
public void methodThaUsesMyDependency() {
...
md.someMethod();
...
}
}
This TestClass is not a spring bean, but needs MyDependency, that is a spring bean. Is there some way I can inject this dependency through Spring, even if I instantiate TestClass with a new operator inside my code?
Thanks
Edit: The method I'm describing in my original answer below is the general way to accomplish DI external of the container. For your specific need - testing - I agree with DJ's answer. It's much more appropriate to use Spring's test support, for example:
#Test
#ContextConfiguration(locations = { "classpath*:**/applicationContext.xml" })
public class MyTest extends AbstractTestNGSpringContextTests {
#Resource
private MyDependency md;
#Test
public void myTest() {
...
While the above example is a TestNG test, there is also Junit support explained in 8.3.7.2. Context management and caching.
General approach: Annotate your class with #Configurable and utilize AspectJ load-time or compile-time weaving. See 6.8.1 in the Spring documentation on AOP for more details.
You can then annotate your instance variables with #Resource or #Autowired. Though they accomplish the same goal of dependency injection, I recommend using #Resource since it's a Java standard rather than Spring-specific.
Lastly, remember to consider using the transient keyword (or #Transient for JPA) if you plan on serializing or persisting the objects in the future. Chances are you don't want to serialize references to your DI'd repository, service, or component beans.
See the autowire() method on the AutowireCapableBeanFactory class. If you use an ClasspathXmlApplicationContext, you can get the factory with getAutowireCapableBeanFactory()
To get the ApplicationContext, you would need to use a static singleton or other central repository, such as JNDI or a Servlet container. See DefaultLocatorFactory on how to get an instance of the ApplicationContext.
If what you need is for testing purposes, Spring has good support for the scenario that you described above.
Check out Spring Reference manual section on Testing

Resources