I have a question regarding testing with SpringJUnit4ClassRunner.
Here is a abbreviated version of code.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations= "/application-config-test.xml")
#WebAppConfiguration
public class TestControllerTest {
#Mock
private TestService testService;
#Autowired
TestDao TestDao;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(new TestController()).build();
}
#Test
public void checkServerName() throws Exception{
TestServer testServer = new TestServer.TestServerBuilder()
.withHostName("test1")
.build();
when(testService.selectTestServer("test1")).thenReturn(testServer);
mockMvc.perform(get("/restapi/test/checkServerName")
.param("serverName", "test1"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(model().size(1))
.andExpect(model().attributeExists())
.andExpect(flash().attribute("message", "success"));
}
}
And here is the config-test.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.directwebremoting.org/schema/spring-dwr
http://www.directwebremoting.org/schema/spring-dwr-2.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.1.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.0.xsd
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<mvc:annotation-driven />
<context:component-scan base-package="com.server.test"/>
<context:component-scan base-package="com.server.db.mybatis"/>
</beans>
Here is controller logic
#GetMapping(value="/checkServerName")
public something checkServerName(#RequestParam (value="serverName")String serverName){
TestServer testServer = testService.selectTestServer(serverName);
if(edgeServer == null){
...
}else{
...
}
return something;
}
The above test fails, firstly because the testService is null.
I want to mock a service so that it answers with a specific object, namely the testServer I made. But somehow it fails, and I've tried #MockBean #InjectMocks #Autowired but it all fails.
How can I mock this service? and suppose that I have successfully mocked this service bean, then can the autowired controller use this mocked bean for executing its logic?
How is defined the testService in the controller?
If it's defined as #Autowired private TestService testService, you need to set your mock testService instead it (the simpler way - RelectionTestUtils.setField(controller, "testService", testService).
If in controller's constructor (it's a proper way - Spring insists on it) just create new controller in test class using mock testService.
After that you can control this testService and test your logic.
you can use mockito to inject dependencies of the controller. #InjectMocks is expected to inject all dependencies of the controller.
For example EmployeeController.java has dependency on EmployeeService.java
e.g:-
public class EmployeeController {
#Autowired
private EmployeeService employeeService;
}
test for the above will look like below
public class EmployeeControllerTest {
private MockMvc mockMvc;
#InjectMocks // careful here you are injecting mocks for the controller. you are not creating mock controller
private EmployeeController controller;
}
Related
I have a problem getting EJB beans working when using Spring's java config classes.
Specifically I have the following that works:
#Configuration
#ComponentScan(basePackages = "com.company.web.config")
#ImportResource(value = {"classpath:spring-beans.xml"})
public class AppConfig {
}
#Configuration
#ComponentScan(basePackages = "com.company.web")
public class WebConfig extends WebMvcConfigurationSupport {
// basic Spring MVC setup omitted
}
My spring-beans.xml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
<jee:local-slsb id="fooService" jndi-name="java:app/model/FooServiceBean!com.company.ejb.FooService"
business-interface="com.company.ejb.FooService" />
</beans>
With this configuration, everything works, and I can do this:
#Controller
public class HomeController {
private final FooService fooService;
#Autowired
public MyPageController(FooService fooService){
this.fooService = fooService;
}
// request methods
}
Now i try to get rid of the XML file. According to the documentation the local-slsb should be equivalent
<bean id="fooService"
class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
<property name="jndiName" value="java:app/model/FooServiceBean!com.company.ejb.FooService"/>
<property name="businessInterface" value="com.company.ejb.FooService"/>
</bean>
However, if I remove the #ImportResource from AppConfig and put this #Bean method instead, deployment fails because the Controller cannot be instantiated (no autowire candidates found for FooService):
#Bean
public LocalStatelessSessionProxyFactoryBean fooService(){
LocalStatelessSessionProxyFactoryBean factory = new LocalStatelessSessionProxyFactoryBean();
factory.setBusinessInterface(FooService.class);
factory.setJndiName("java:app/model/FooServiceBean!com.company.ejb.FooService");
return factory;
}
Any ideas why this doesn't work? I am using Spring version 4.0.2.
It seems that the issue was related to the order in which configuration was read, and possibly double configuration loading.
Specifically, introducing a separate configuration class and importing it before WebConfig seems to do the trick, like so:
#Configuration
#Import({EJBConfig.class, WebConfig.class})
public class AppConfig {
}
#Configuration
public class EJBConfig {
#Bean
public LocalStatelessSessionProxyFactoryBean fooService(){
LocalStatelessSessionProxyFactoryBean factory = new LocalStatelessSessionProxyFactoryBean();
factory.setBusinessInterface(FooService.class);
factory.setJndiName("java:app/model/FooServiceBean!com.company.ejb.FooService");
return factory;
}
}
#Configuration
#ComponentScan(basePackages = "com.company.web")
public class WebConfig extends WebMvcConfigurationSupport {
// basic Spring MVC setup omitted
}
Here are the files that I use:
component.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">
<context:component-scan
base-package="controllers,services,dao,org.springframework.jndi" />
</beans>
ServiceImpl.java
#org.springframework.stereotype.Service
public class ServiceImpl implements MyService {
#Autowired
private MyDAO myDAO;
public void getData() {...}
}
ServiceImplTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath*:conf/components.xml")
public class ServiceImplTest{
#Test
public void testMyFunction() {...}
}
Error:
16:22:48.753 [main] ERROR o.s.test.context.TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener#2092dcdb] to prepare test instance [services.ServiceImplTest#9e1be92]
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'services.ServiceImplTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private services.ServiceImpl services.ServiceImplTest.publishedServiceImpl; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [services.ServiceImpl] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:287) ~[spring-beans.jar:3.1.2.RELEASE]
Make sure you have imported the correct package. If I remeber correctly there are two different packages for Autowiring. Should be :org.springframework.beans.factory.annotation.Autowired;
Also this looks wierd to me :
#ContextConfiguration("classpath*:conf/components.xml")
Here is an example that works fine for me :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/applicationContext_mock.xml" })
public class OwnerIntegrationTest {
#Autowired
OwnerService ownerService;
#Before
public void setup() {
ownerService.cleanList();
}
#Test
public void testOwners() {
Owner owner = new Owner("Bengt", "Karlsson", "Ankavägen 3");
owner = ownerService.createOwner(owner);
assertEquals("Check firstName : ", "Bengt", owner.getFirstName());
assertTrue("Check that Id exist: ", owner.getId() > 0);
owner.setLastName("Larsson");
ownerService.updateOwner(owner);
owner = ownerService.getOwner(owner.getId());
assertEquals("Name is changed", "Larsson", owner.getLastName());
}
I've done it with two annotations for test class: #RunWith(SpringRunner.class) and #SpringBootTest.
Example:
#RunWith(SpringRunner.class )
#SpringBootTest
public class ProtocolTransactionServiceTest {
#Autowired
private ProtocolTransactionService protocolTransactionService;
}
#SpringBootTest loads the whole context, which was OK in my case.
A JUnit4 test with Autowired and bean mocking (Mockito):
// JUnit starts with spring context
#RunWith(SpringRunner.class)
// spring loads context configuration from AppConfig class
#ContextConfiguration(classes = AppConfig.class)
// overriding some properties with test values if you need
#TestPropertySource(properties = {
"spring.someConfigValue=your-test-value",
})
public class PersonServiceTest {
#MockBean
private PersonRepository repository;
#Autowired
private PersonService personService; // uses PersonRepository
#Test
public void testSomething() {
// using Mockito
when(repository.findByName(any())).thenReturn(Collection.emptyList());
Person person = new Person();
person.setName(null);
// when
boolean found = personService.checkSomething(person);
// then
assertTrue(found, "Something is wrong");
}
}
For Spring 5.x and JUnit 5, writing unit tests is quite different.
We have to use #ExtendWith to register Spring extensions (SpringExtension). This brings Spring into play which activates part of the application context (instantiates and manages beans from selected configuration classes).
Note that this is different from the effect of #SpringBootTest which loads the full application context (and IMHO cannot be considered a unit test).
For example, let's create a configuration class FooConfig which produces a bean named foo1:
#Configuration
public class FooConfig
{
#Bean
public String foo1() { return "Foo1"; }
}
Now, let's write a JUnit 5 test case which injects foo1:
import static org.junit.jupiter.api.Assertions.*;
// ... more imports ...
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {
helloworld.config.FooConfig.class,
})
class FooTests
{
#BeforeAll
static void setUpBeforeClass() throws Exception {}
#AfterAll
static void tearDownAfterClass() throws Exception {}
#BeforeEach
void setUp() throws Exception {}
#AfterEach
void tearDown() throws Exception {}
#Autowired
private String foo1;
#Test
void test()
{
assertNotNull(foo1);
System.err.println(foo1);
}
}
In Spring 2.1.5 at least, the XML file can be conveniently replaced by annotations.
Piggy backing on #Sembrano's answer, I have this. "Look ma, no XML".
It appears I to had list all the classes I need #Autowired in the #ComponentScan
#RunWith(SpringJUnit4ClassRunner.class)
#ComponentScan(
basePackageClasses = {
OwnerService.class
})
#EnableAutoConfiguration
public class OwnerIntegrationTest {
#Autowired
OwnerService ownerService;
#Test
public void testOwnerService() {
Assert.assertNotNull(ownerService);
}
}
I think somewhere in your codebase are you #Autowiring the concrete class ServiceImpl where you should be autowiring it's interface (presumably MyService).
You should make another XML-spring configuration file in your test resource folder or just copy the old one, it looks fine, but if you're trying to start a web context for testing a micro service, just put the following code as your master test class and inherits from that:
#WebAppConfiguration
#RunWith(SpringRunner.class)
#ContextConfiguration(locations = "classpath*:spring-test-config.xml")
public abstract class AbstractRestTest {
#Autowired
private WebApplicationContext wac;
}
You can use #Import annotation. It's more readable and shorter than using the #ComponentScan annotation.
import org.springframework.context.annotation.Import;
#Import(value = {MyService.class, MyInjectedService.class})
public class JUnitTest {
#Autowired
private MyService service;
// ...
}
I have built application that uses MongoDB and I have came across problem with testing.
As long as I used JPA and some relational database I used some test layer which switched persistence to in-memory database (linke HSQLDB or MySQL) for test purposes. This way I was able to limit IO operations and speed up tests.
However with MongoDB and Spring Data it is very convenient to use repositories based on interfaces extending MongoRepository.
My question is how to deal with unit testing and functional testing when using repositories? For example I have simple class that is anotated as mongo document:
public class Company {
#Id
private String id;
#NotEmpty
private String name;
private String description;
private String website;
private String logo;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getWebsite() {
return website;
}
public void setWebsite(String website) {
this.website = website;
}
public String getLogo() {
return logo;
}
public void setLogo(String logo) {
this.logo = logo;
}
}
and related repository:
#Repository
public interface CompanyRepository extends MongoRepository<Company, Serializable> {
}
It is used in coresponding controller:
#RestController
public class CompanyController {
private static final Logger log = LoggerFactory.getLogger(CompanyController.class);
#Autowired
private CompanyRepository repository;
#RequestMapping(value = "/company", method = RequestMethod.POST)
public void create(#Valid #RequestBody(required = true) Company company) {
repository.save(company);
}
}
Finally I made two tests (however it could be one covering both tasks) that covers controller api and data format (where I mock repository to prevent from IO operations, and it works nice), and second where I want to make sure that passed object was persisted successfuly. As it cames out it is not so simple. First of all (correct me if I'm wrong) there is no in-memory mongo implementation. Secondly, I cannot verify with mockito execution of save method because of inheritance I suppose.
#ContextConfiguration(classes = Application.class)
#WebAppConfiguration
public class CompanyControllerNGTest extends AbstractTestNGSpringContextTests {
#Mock
private CompanyRepository repositoryMock;
#Autowired
#InjectMocks
private CompanyController controller;
private MockMvc mockMvc;
#BeforeMethod
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#DataProvider
public static Object[][] companyJsonProvider() {
return new Object[][]{
{Json.createObjectBuilder()
.add("name", "JakasFirma")
.add("description", "jakas firma opis")
.add("website", "www.jakasfirma.com")
.add("logo", "jakies logo")
.build(), status().isOk()},
{Json.createObjectBuilder()
.add("name", "JakasFirma")
.build(), status().isOk()},
{Json.createObjectBuilder()
.add("description", "jakas firma opis")
.add("website", "www.jakasfirma.com")
.add("logo", "jakies logo")
.build(), status().isBadRequest()},
{Json.createObjectBuilder()
.build(), status().isBadRequest()},
};
}
#Test(dataProvider = "companyJsonProvider", enabled = false)
public void apiTest(JsonObject companyJson, ResultMatcher expectedStatus) throws Exception {
//given
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
String content = companyJson.toString();
//when
mockMvc.perform(post("/company").contentType(MediaType.APPLICATION_JSON).content(content)).
//then
andExpect(expectedStatus);
}
#Test
public void shouldCreate() throws Exception {
//given
//when
controller.create(mock(Company.class));
//then
//verify(repositoryMock, times(1)).save(any(Iterable.class));
}
}
I thought about introducing a dao layer between controller and repository that could be mocked and verified bot it adds more complexity and force to encapsulate each method used by repository. Also it doesn't fix problem but partially moves it to lower level.
Is there any method or practice that could help to deal with this kind of problem? Or maybe I should use different approach with mongo?
For unit testing I would stub or write an implementation of CompanyRepository and inject that into your controller (you may need to add a Setter method for CompanyRepository).
For Functional or integration testing I would look at using the following
#ContextConfiguration("file:my-context-file.xml")
#RunWith(SpringJUnit4ClassRunner.class)
In the context file I would expect you to configure the beans that are only required for your test to run.
I have faced the same problem. I recommend you NOT to use MongoRepository to access to MongoDB for several reasons:
It is used for easy queries, like findByxxx or findOneByxxxAndyyy. If you want more complex query, even #Query cannot give you what you want.
It is easy to make syntax mistake, because all query conditions are defined by interface.
Cannot define dynamic queries, like search depending on context, fields, aggregations, etc...
Instead, I recommend you to use MongoOperations. It accepts dynamic query with simple/complex criteria, very easy syntax to write your query, etc... For mocking, use Fongo framework, so it is 100% in memory database, so you can do all CRUD operations for asserts...
More info: http://docs.spring.io/spring-data/data-mongo/docs/current/api/org/springframework/data/mongodb/core/MongoOperations.html
How to mock
https://github.com/fakemongo/fongo#usage-details
UPDATE:
If you still need to mock MongoDB repository, use this xml definition:
mongo-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd">
<bean name="fongo" class="com.github.fakemongo.Fongo">
<constructor-arg value="InMemoryMongo" />
</bean>
<bean id="mongo" factory-bean="fongo" factory-method="getMongo" />
<mongo:db-factory id="mongoDbFactory" mongo-ref="mongo" />
<!-- localhost settings for mongo -->
<!--<mongo:db-factory id="mongoDbFactory" /> -->
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoDbFactory" />
</bean>
<!-- Base package to scan the mongo repositories -->
<!-- Set your CompanyRepository package -->
<mongo:repositories base-package="package.to.repositories" />
</beans>
Define your test class by this way:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/mongo-config.xml"})
public class CompanyTest {
#Autowired
private MongoOperations mongoOperations;
#Resource
private CompanyRepository companyRepository;
#Test
public void foo() {
// Define test logic
}
}
My Aspect class will be ,
#Configuration
#EnableAspectJAutoProxy
#Component
#Aspect
public class AspectClass {
#Before("execution(* com.pointel.aop.test1.AopTest.beforeAspect())")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before running the beforeAspect() in the AopTest.java class!");
System.out.println("Hijacked Method name : " + joinPoint.getSignature().getName());
System.out.println("************************");
}
}
My other java Class
public class AopTest {
public void beforeAspect() {
System.out.println("This is beforeAspect() !");
}
}
My Main Class is
public class MainMethod {
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext("ApplicationContext/applicationContext.xml");
AopTest test = (AopTest)context.getBean("bean1");
test.beforeAspect();
}
}
My applicationContext.xml is ,
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<bean id="bean1" class="com.pointel.aop.test1.AopTest" />
</beans>
In this the #Before("execution(* com.pointel.aop.test1.AopTest.beforeAspect())") in the AspectClass will not be executed before the beforeAspect() in the AopTest , when running Main method.
Good answers are definitely appreciated.
First of all if you're going to use an annotation based configuration, use AnnotationConfigApplicationContext instead of FileSystemXmlApplicationContext. And get rid of the applicationContext.xml file and simply add a #Bean method in your configuration class. Something like this:
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages = "your.aspect.package")
public class AspectConfig {
#Bean
public AopTest aopTest() {
return new AopTest();
}
}
In your main
public class MainMethod {
public static void main(String[] args) {
AnnotationConfigApplicationContextcontext = new AnnotationConfigApplicationContext(AspectConfig.class);
// don't forget to refresh
context.refresh();
AopTest test = (AopTest)context.getBean("aopTest");
test.beforeAspect();
}
}
In AspectClass you should have #Component, #Aspect, and your method should have the advice or pointcut annotation like #Before. It needs to be a #Component, so that Spring knows to scan it.
Here some code need to add in xml to use annotations-
1.for #component annotation.
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
2.after that use component scan to get all annotated bean class which use #component annotation,and use aop autoproxy-
<context:annotation-config/>
<context:component-scan base-package="mypackage"></context:component-scan>
<aop:aspectj-autoproxy>
</aop:aspectj-autoproxy>
for examples visit-www.technicaltoday.com/p/spring.html
You are missing the point cut definition in your aspect class.
For example;
#Pointcut("execution(* *.advice(..))")
public void logBefore(){}
#Before("logBefore()")
public void beforeAdvicing(){
System.out.println("Listen Up!!!!");
}
You first have to defin the point to weave your aspect to. You do this by using Point cuts.It is the point cut name you give within your #Before annotation. Have a look at my blog post for more information # http://dinukaroshan.blogspot.com/2010/06/aop-with-spring.html
I don't see your AspectClass in the beans configuration. You should also declare it as a Bean.
I am trying to get Aspect working with Spring 3 and annotations.
#Aspect
public class AttributeAspect {
#Pointcut("#annotation(com.mak.selective.annotation.Attribute)")
public void process(){
System.out.println("Inside Process ....");
}
#Around("process()")
public void processAttribute(){
System.out.println("Inside Actual Aspect ..");
}
}
XML:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<aop:aspectj-autoproxy proxy-target-class="false" />
<context:component-scan base-package="com.mak.selective.annotation.*" />
<bean name="attribute" class="com.mak.selective.annotation.AttributeAspect"/>
</beans>
MY Test to test the Aspect:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/springcontext/*.xml")
public class AttributeTest {
#Attribute(tableName = "firstTable", columnName = "New Column")
private void getAttribute() {
System.out.println("Inside Attribute call...");
}
#Test
public void testAttributeAspect() {
getAttribute();
}
}
With this code i can only see "Inside Attribute call..." but nothing from Aspect.
Please guide.
Got this working by making a new Object (Component) and injected to the Junit test class.
Good to see that you got it working from XML, but you could have also done it from annotations.
The issue is that the #Aspect annotation is not a Spring stereotype, so the scanner is not registering the aspect as a Spring Bean. Just add either #Service or #Component above or below #Aspect and it will be registered.
Also, either directly name the bean (e.g., #Service("myNamedService")) or have it implement an interface (e.g., public class AttributeAspect implements IAspect {), as per standard Spring design.
You need to use real AspectJ if you want to intercept invocations of methods within the same bean form where it is invoked. (What you have done, should work if the method testAttributeAspect() is located in an other bean.)
How to do real AspectJ?
Using the AspectJ compiler and weaver enables use of the full AspectJ language, and is discussed in Section 7.8, “Using AspectJ with Spring applications”.
#See Spring Reference
A few things:
Firstly, when you do around advice you need to write the advice method like this:
#Around(...)
public void aroundAdviceMethod(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("before...");
pjp.proceed();
}
finally {
System.out.println("After...");
}
}
But also (and this at least applies when you're using proxies, not entirely sure in your case), the method you're putting advice on needs to be public (yours isn't), spring managed (via #Component or otherwise) and called external from the class so the proxy can take effect (also not the case in your example). So you need something like this:
#Component
public class SomeClass {
#Attribute
public void someMethodCall() {
System.out.println("In method call");
}
}
public class SomeUnitTest {
#Autowired SomeClass someClass;
#Test
public void testAspect() {
someClass.someMethodCall();
}
}