Testing out Aspect #Before annotation in a Spring Boot application and it is not working. No Errors thrown. Just simply not working. It is a very simple rest service as below. Been at it for some time now and any tutorials and answers here seems to point that this is correct. My rest service returns correctly. Just that the message from Aspect doesn't print. I am very confident my package structure is correct. Please advice.
#SpringBootApplication
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class StuffApplication {
public static void main(String[] args) {
SpringApplication.run(StuffApplication.class, args);
}
}
#RestController
#RequestMapping("/information")
public class ContentController {
#GetMapping("/person")
public People getPerson(Model model){
log(); // expecting the aspect print to occur when I call this.
// some logic to get personList which works fine
return new People(personList);
}
public void log(){
System.out.println("This is a logger");
}
}
#Aspect
#Component
public class JsonAspect {
#Before(value = "execution(* com.example.stuff.controller.ContentController.log())")
public void printBefore(){
System.out.println("I was called before the logger"); // never prints when I call log method
}
}
Related
I have an issue with rollbacking in #Transactional. When rollback is called, rollback itself is working, but another method after is executed.
#Transactional(rollbackFor = { Exception.class })
#Service
public class Service {
public void method1() {
stuffToGetListOfObjects();
deleteAllAndSaveAll(listOfObejcts);
Util.staticMethod();
}
private deleteAllAndSaveAll(List list) {
repository.deleteAll();
repository.saveAll(list);
}
}
#SpringBootApplication
public class Application implements ApplicationRunner {
private final Service service;
#Autowired
public Application(Service service) {
this.service = service;
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
service.method1();
}
}
When something goes wrong during the insertion in repository.saveAll(list), no data is actually deleted which is fine and expected. The issue is program is going on the Util.staticMethod() method is executed "after" rollback.
I know that static methods cannot be #Transactional and private methods are ignored, but that doesn't seem the issue here. According to the log, everything in method1() is executed first and after that, the inserting is happening. I guess I need to pick out the Util.staticMethod() calling from transaction somehow.
That's why you should separate the repository actions from the service functions. Repository actions are rollbacked. If you separate them the repository level will throw an error and rollback itself. And before the static method, you can check whether the transaction is finished or not by #transactionaleventlistener.
I want to use a mechanism for create a one time compute function. I try to use Spring Caching. But it does not working. Please help me to solve this problem. My code like as below,
Gradle Dependency
compile 'org.springframework.boot:spring-boot-starter-cache'
Main Class of Spring Boot Application
#SpringBootApplication
#EnableCaching
public class Application {
public static ApplicationContext applicationContext;
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
// todo: Try to save response text and request body
applicationContext = SpringApplication.run(Application.class, args);
}
#Bean
WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/**")
.addResourceLocations("classpath:/")
.setCacheControl(CacheControl.maxAge(3600, TimeUnit.SECONDS).noTransform().mustRevalidate());
}
};
}
}
My Coputational property and Test method
public String test(){
return hello();
}
#Cacheable("hello")
public String hello(){
System.out.println("hello");
return "Hello";
}
The #Cacheable annotation caches the values when it is called from outside your #Bean so calling it from another method inside your bean will not work.
try something like
#Bean
public class CachingBean {
#Cacheable("hello")
public String hello(){
System.out.println("hello");
return "Hello";
}
}
#Service
public class CallerService {
#Autowired
private CachingBean cachingBean;
// Setters, constructors...
public String test(){
return cachingBean.hello();
}
}
And then it should work.
That's because the #Cacheable annotation creates a proxy around the method call when it is injected as a Bean, so a direct call (to an instance created directly) or an internal call are not intercepted and the caching mechanism does not even see those calls.
I still sometimes forget about it and get biten by it at the beginning :).
Cheers!
I am trying to integrate AspectJ and SpringBoot. It was running fine until I tried experimenting a bit and used:
#SpringBootApplication
public class TestClassRunner {
#MyAnnotation
public void someDisplay(){
System.out.println("My Display");
}
#Bean(name="dummyString")
public String getString(){
someDisplay();
return "SUCCESS";
}
}
The Aspect class is defined as:
#Aspect
#Component
public class MyAnnotationProcessor{
#Before("#annotation(myTest.MyAnnotation)")
public void aroundSampleCreation(JoinPoint joinPoint) throws Throwable {
System.out.println(joinPoint.getSignature());
System.out.println("Executing the Before call");
}
}
Now this Advice is not getting executed. Is it because of some special character of the #Configuration class (I know #SpringBoot uses that internally)?
I have included all the dependencies and that should not be the cause of this not working.
Any help is highly appreciated.
Does anyone knows how to add a test Resource (i.e. one that is only for testing purposes and not added in run() method of the app)?
Here is an example:
public class MyTest {
#ClassRule
public static final DropwizardAppRule<TestConfiguration> RULE =
new DropwizardAppRule<TestConfiguration>(MyApp.class, "my-app-config.yaml");
#BeforeClass
public static void setUpBeforeClass() throws Exception
{
MyTest.RULE.getEnvironment().jersey().register(new JustForTestingResource());
}
#Test
public final void testTestResource()
{
Client client = new Client();
ClientResponse response = client.resource(
String.format("http://localhost:%d/rest/v1/test", RULE.getLocalPort()))
.get(ClientResponse.class);
assertThat(response.getStatus(), is(200));
}
}
and
public class JustForTestingRessource {
#GET
#Path("test")
#Produces(MediaType.APPLICATION_JSON)
public Response getInTestResource()
{
return Response.status(Status.OK).type(MediaType.TEXT_PLAIN).entity("get #Path(\"test\") is ok").build();
}
}
My problem is that the added resource is not added and I get resource not found 404 error response. It seems that I am registering the new resource after resource publishing and there is no refresh inside Dropwizard after start.
I dont want to extend my Application class and I dont want to insert test code into my real application code. Does anyone knows how to register the test resource without registering it in run() method of the Application?
This works, but a new class is needed:
public class TestService extends MyService{
#Override
public void run(
TestConfigurationconfiguration,
Environment environment) throws ClassNotFoundException
{
environment.jersey().register(new JustForTestingRessource());
super.run(configuration,environment);
}
}
Call in JUnit as already known:
#ClassRule
public static DropwizardAppRule<TestConfiguration> RULE =
new DropwizardAppRule<TestConfiguration>(TestService.class, "my-app-config.yaml");
Edit: Removing previous answer because it didn't solve your problem the way you wanted to do it.
I dug into the environment startup code and realized the reason why registering a controller didn't make it available is because jetty had already been started. If you stop jetty, register your controller and start jetty back up again, your resource will be available and you can use it in your test.
#BeforeClass
public static void setUpBeforeClass() throws Exception
{
MyTest.RULE.environment.applicationContext.stop()
MyTest.RULE.environment.jersey().register(new JustForTestingResource())
MyTest.RULE.environment.applicationContext.start()
}
You can test the Resource itself in a Jersey Container without starting a full dw-instance.
Check the "Testing Resources" section.
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
public class PersonResourceTest {
private static final PeopleStore dao = mock(PeopleStore.class);
#ClassRule
public static final ResourceTestRule resources = ResourceTestRule.builder()
.addResource(new PersonResource(dao))
.build();
private final Person person = new Person("blah", "blah#example.com");
#Before
public void setup() {
when(dao.fetchPerson(eq("blah"))).thenReturn(person);
// we have to reset the mock after each test because of the
// #ClassRule, or use a #Rule as mentioned below.
reset(dao);
}
#Test
public void testGetPerson() {
assertThat(resources.client().resource("/person/blah").get(Person.class))
.isEqualTo(person);
verify(dao).fetchPerson("blah");
}
}
I had the similar issue with the #ClassRule, maybe it can help to somebody..
In my test (Groovy) the invocation of RULE.getApplication() or getEnvironment() from #BeforeClass method returned null:
def setupSpec() {
RULE.application.run()
}
shown
java.lang.NullPointerException: Cannot invoke method run() on null object
I.e. RULE.testSupport had both null application and environment.
I found out that the call to
RULE.testSupport.before()
just before run() solves the error:
def setupSpec() {
RULE.testSupport.before()
RULE.application.run()
}
And then #AfterClass method:
def cleanupSpec() {
RULE.testSupport.after()
}
Or just use #Rule instead of #ClassRule and call
def setup() {
RULE.application.run()
}
inside of #Before method instead of #BeforeClass.
Though It seems strange, maybe there is some other better solution exists..
public class TestMain extends Main{
public static void main(String ... args) throws Exception {
new TestMain().run(args);
}
#Override
public void initialize(Bootstrap<AppConfiguration> bootstrap) {
super.initialize(bootstrap);
bootstrap.addBundle(
new MigrationsBundle<AppConfiguration>() {
#Override
public DataSourceFactory getDataSourceFactory(
AppConfiguration configuration) {
return configuration.getDataSourceFactory();
}
});
}
}
I am writing a Rest service using Spring MVC. Here is the outline of the class:
#Controller
public class MyController{
#RequestMapping(..)
public void myMethod(...) throws NotAuthorizedException{...}
#ExceptionHandler(NotAuthorizedException.class)
#ResponseStatus(value=HttpStatus.UNAUTHORIZED, reason="blah")
public void handler(...){...}
}
I have written my unit tests using the design posted here. The test is basically as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(....)
public class mytest{
MockHttpServletRequest requestMock;
MockHttpServletResponse responseMock;
AnnotationMethodHandlerAdapter handlerAdapter;
#Before
public void setUp() {
requestMock = new MockHttpServletRequest();
requestMock.setContentType(MediaType.APPLICATION_JSON_VALUE);
requestMock.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
responseMock = new MockHttpServletResponse();
handlerAdapter = new AnnotationMethodHandlerAdapter();
}
#Test
public void testExceptionHandler(){
// setup ....
handlerAdapter.handle(...);
// verify
// I would like to do the following
assertThat(responseMock.getStatus(), is(HttpStatus.UNAUTHORIZED.value()));
}
}
However, the call to handle is throwing the NotAuthorizedException. I have read that this is by design to be able to unit test that the method throws the appropriate exception, however I would like to write an automated test that the framework is handling this exception appropriately and that the class under test has implemented the handler appropriately. Is there a way to do this?
Please be aware that I do not have access to the actual code in a place where I could post it.
Also, I am limited (for unfortunate reasons) to Spring 3.0.5 or 3.1.2.
Consider using Spring 3.2 and its mvc-test-framework
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration("file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml")
public class WebMvcTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void getFoo() throws Exception {
this.mockMvc.perform(
get("/testx")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
)
.andExpect(status().isUnauthorized());
}
}
Controller code
#Controller
public class MyController {
public class MyException extends RuntimeException {
};
#RequestMapping("/testx")
public void myMethod() {
throw new MyException();
}
#ExceptionHandler(MyException.class)
#ResponseStatus(value = HttpStatus.UNAUTHORIZED, reason = "blah")
public void handler() {
System.out.println("handler processed");
}
}
This "test" passes well.
Disclaimer: currently I'm a noob in Spring MVC testing, actually it's my first test.
upd: Thanks to The Drake for the correction.
Annotate your Exception Handling controller with #ControllerAdvice instead of #Controller.
As Boris Treukhov noted when adding the #ExceptionHandler annotation to a method in the controller that throws the exception will make it work but only from that specific controller.
#ControllerAdvice will allow your exception handeling methods to be applicable for your whole application not just one specific controller.
You could change #Test to
#Test(expected=NotAuthorizedException.class)
This would return true if the internals throw up that exception and false otherwise.
This would also make the assertThat() unnecessary. You could write a second test that catches the NotAuthorizedException then you could inspect the responseMock under that condition then.