Unit testing with mongodb repositories - java

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
}
}

Related

Testing Controller logic that contains service with SpringJUnit4ClassRunner

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;
}

How to use #Value on Spring non-managed class

Here my (simplified) code before explaining my problem :
foo.bar.MyFile
public class MyFile extends MyFileAbstract {
#Value("${FILE_PATH}")
private String path;
[...]
public MyFile(final Date date, final String number, final List<MyElement> elements) {
this.date = date;
this.number = number;
this.elements = elements;
}
#Override
public String getPath() {
return path;
}
[...]
}
foo.bar.MyService
#Service
public class MyService {
[...]
public String createFolder(MyFileAbstract file) throws TechnicalException {
[...]
String path = file.getPath();
[...]
}
[...]
}
the call of service
[...]
#Autowired
MyService service;
public void MyMethod() {
MyFile file = new MyFile();
service.createFolder(file);
[...]
}
[...]
I use a context XML to configure Spring :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<context:property-placeholder
file-encoding="utf-8"
location="file:///[...]/MyProperties.properties" />
<context:annotation-config />
<context:component-scan base-package="foo.bar.classes" />
[...]
</beans>
The problem is that the variable path is null at runtime in both MyService and MyFile file when a instantiate MyFile to call my service MyService.
I am looking a solution to inject my property ${FILE_PATH} inside MyFile.
Here my environment :
Apache Tomcat 7
Java 8
Spring 4.1.6.RELEASE
I have seen that Spring AOP with #Configurable bean could resolve this but don't want to change my Java Agent because I don't want to modify the configuration on the production server.
And I don't know how to use #Service on MyFile with my custom constructor.
Any idea is welcome.
You can add to your MyService
#Autowired
private Environment environment;
and just get the value
environment.getProperty("FILE_PATH");
After that you can set it to the file if necessary.
#Service
public class BeanUtilityService implements ApplicationContextAware {
#Autowired
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Create a Utility class as a service , create a static method and get the bean from the context.Then use that bean to get the properties required
use #PropertySource annotation
#PropertySource("classpath:config.properties") //use your property file name
public class MyFile extends MyFileAbstract {
#Value("${FILE_PATH}")
private String path;
[...]
public MyFile(final Date date, final String number, final List<MyElement> elements) {
this.date = date;
this.number = number;
this.elements = elements;
}
#Override
public String getPath() {
return path;
}
[...]
}

Spring dynamic language support with transactions

In the current application I am working on we feel the need to re-evaluate some DAO logic on the fly, without the need for a new deploy.
For this I choose to try the Spring-Groovy integration.
I've got the beans wired and in the Groovy scripts I can get a handle of the the javax.sql.Datasource object.
But I have a problem when a transactional operation happens, for instance if I make a insert in some java-style DAO the row is not visible in the groovy-style DAO even if it's called from the same service marked as #Transactional.
I will try to put some relevant piece of code, if any other details are needed I can provide.
public class App {
public static void main(String[] args) {
final AbstractApplicationContext ctx = new AnnotationConfigApplicationContext(BaseConfig.class);
TestService ts = ctx.getBean(TestService.class);
ts.testGroovy();
}
}
#Service
#Transactional
public class TestServiceImpl implements TestService {
#Autowired
private HibernateDAO hibernateDAO;
#Autowired
private OtherDAO otherDAO;
#Autowired
private Groovy groovy;
#Override
public void testGroovy() {
hibernateDAO.makeInsert(); //actually now it makes a jdbc insert, no need for flush session
//throw new RuntimeException("error");
groovy.testInjection();
otherDAO.verifyInsert();
}
}
The java DAO:
#Repository
public class HibernateDAOImpl implements HibernateDAO{
#Autowired
private DataSource dataSource;
#Override
public void makeInsert() {
Connection connection = DataSourceUtils.getConnection(dataSource);
try {
PreparedStatement ps = connection.prepareStatement("insert into GROOVY_TEST values(?,?)");
ps.setLong(1, System.currentTimeMillis());
ps.setString(2, UUID.randomUUID().toString());
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
The interface used for proxy:
public interface Groovy {
void testInjection();
}
For injecting the Datasource in Groovy I extended a java class that autowired the Datasource instance.
public abstract class GroovyHelper implements Groovy{
#Autowired
private DataSource dataSource;
public DataSource getDataSource() {
return dataSource;
}
}
The groovy file:
import ro.asf.groovy.GroovyHelper
import javax.sql.DataSource
import groovy.sql.Sql
class GroovyImpl extends GroovyHelper {
void testInjection() {
//throw new RuntimeException("error")
Sql sql = new Sql(dataSource)
sql.eachRow('''SELECT * FROM GROOVY_TEST ''', { article ->
println article.value
})
}
}
And the xml file for wiring the groovy script to spring:
<?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:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd">
<lang:groovy id="groovy" script-source="file:scripts/Groovy.groovy" />
</beans>
In the verify DAO layer the insert is seen as expected.
Thank you,
Daniel

Spring 4.1.6 - Load ".properties" and initialize a bean with that

I'm new with Spring MVC and I'm doing some tests. I was trying to find some answers about this issues, but most of them make references to Spring 3.11 and I'm using the last release: 4.1.6.
I want to load a ".properties" file when the application starts, and use the information in it to create a bean to access it in all the context of the app.
So far, I reach to load the file in the servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
...
<context:property-placeholder location="classpath*:resources/Resources.properties" />
</beans:beans>
I think (not really sure) that I correctly declared the bean in the root-context.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<bean id="Resources" class="ar.com.violenciaesmentir.blog.resources.ResourcesDB"/>
</beans>
And I also think I made the bean correctly, but I don't really know if the annotations are right:
package ar.com.violenciaesmentir.blog.resources;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
#Service
public class ResourcesDB {
#Value("DB.NAME")
private String name;
#Value("DB.TYPE")
private String type;
#Value("DB.USER")
private String user;
#Value("DB.PASS")
private String pass;
#Value("DB.DRIVER")
private String driver;
#Value("DB.URL")
private String url;
#Value("DB.MAXACTIVE")
private String maxActive;
#Value("DB.MAXIDLE")
private String maxIdle;
#Value("DB.MAXWAIT")
private String maxWait;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getMaxActive() {
return maxActive;
}
public void setMaxActive(String maxActive) {
this.maxActive = maxActive;
}
public String getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(String maxIdle) {
this.maxIdle = maxIdle;
}
public String getMaxWait() {
return maxWait;
}
public void setMaxWait(String maxWait) {
this.maxWait = maxWait;
}
}
My ".properties" file:
DB.NAME = jdbc/Blog
DB.TYPE = javax.sql.DataSource
DB.USER = blog
DB.PASS = blog
DB.DRIVER = oracle.jdbc.driver.OracleDriver
DB.URL = jdbc:oracle:thin:#localhost:1521:xe
DB.MAXACTIVE = 20
DB.MAXIDLE = 5
DB.MAXWAIT = 10000
I think the reference is ok because it gave me troubles when starting the server, saying that it couldn't find the property for "name", but I was doing the annotation wrong and then I fixed.
What I want is to have that bean initialized and be avaible to have an attribute in the DB class like:
#ManagedAttribute
private ResourcesDB resources;
...
public void foo() {
String dbName = resources.getName();
}
When I try it, resources is null. What I'm doing wrong?
-----UPDATE-----
Ok, I could solve the problem doing some try&fail with the answers given. First of all, I corrected the #Value like ("${DB.NAME}") and added a value to the service annotation #Service(value="Resources").
Then, the only change I got to do was in the servlet-context.xml. Instead of:
<context:property-placeholder location="classpath*:resources/Resources.properties" />
I used:
<beans:bean id="configuracion" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<beans:property name="location" value="classpath:Resources.properties"/>
</beans:bean>
And used #Autowire instead of #ManagedBean to access the bean.
There are 2 things flawed in your code.
Your #Value expressions are wrong
Your <context:property-placeholder /> must be in the same context as the beans
When using #Value you have to use placeholders, by default ${property name} you are just using a name. So update your annotations to reflect that. I.e. #Value("${DB.NAME}.
Next you have defined <context:property-placeholder /> in the context loaded by the DispatcherServlet whereas your beans are loaded by the ContextLoaderListener. The property placeholder bean is a BeanFactoryPostProcessor and it will only operate on bean definitions loaded in the same context. Basically your bean definition are in the parent context and your placeholder in the child context.
To fix move <context:property-placeholder /> to the same context where you bean is defined in.
Instead of #ManagedAttribute which is a JSF annotation use #Autowired or #Inject. And if you don't have a <context:component-scan /> add a <context:annotation-driven />.
Your #Value syntax is incorrect. It should be #Value("${DB.NAME}").
You might also need to add this to your XML config:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:resources/Resources.properties" />
</bean>
The value on the location may vary, not sure on how you are structuring and building your artifacts.

AOP with Spring 3 using Annotations

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();
}
}

Categories

Resources