I'm developing an application which uses AspectJ with Java. In development, I use ajc and java together. AspectJ calls some code segments when necessary and I want to test these code segments called by AspectJ. I tried to do it with Mockito but I failed, does anyone know any other way to test it?
I am not sure on how to do it in plain Java and JUnit, but if you have access to Spring-Integration-Test you can have an easy approach with the MockMVC and support classes that it offers.
And bellow you can see an example in which I am testing a controller that has an Aspect wrapped around it:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration
public class ControllerWithAspectTest {
#Autowired
private WebApplicationContext wac;
#Autowired
private MockMvc mockMvc;
#Autowired
#InjectMocks
private MongoController mongoController;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
// if you want to inject mocks into your controller
MockitoAnnotations.initMocks(this);
}
#Test
public void testControllerWithAspect() throws Exception {
MvcResult result = mockMvc
.perform(
MockMvcRequestBuilders.get("/my/get/url")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
}
#Configuration
#EnableWebMvc
#EnableAspectJAutoProxy(proxyTargetClass = true)
static class Config extends WebMvcConfigurerAdapter {
#Bean
public MongoAuditingAspect getAuditingAspect() {
return new MongoAuditingAspect();
}
}
}
You can use the approach above even if you don't have Spring configured in your application, as the approach I've used will allow you to have a configuration class (can and should be a public class residing in it's own file).
And if the #Configuration class is annotated with #EnableAspectJAutoProxy(proxyTargetClass = true), Spring will know that it needs to enable aspects in your test/application.
If you need any extra clarification I will provide it with further edits.
EDIT:
The Maven Spring-Test dependency is:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
I just created a JUnit4 Runner to allow AspectJ Load Time Weaving on JUnit test cases. Here is a simple example:
I created a HelloService to return a greeting. And I created an Aspect to make names in the greeting upper case. Finally i created a unit test to use the HelloService with a lower case name and expect an upper case result.
All the details of the example are part of the GitHub project for reference:
https://github.com/david-888/aspectj-junit-runner
Just include the most up to date aspectj-junit-runner JAR in your classpath. Then your tests may look like this:
#AspectJConfig(classpathAdditions = "src/test/hello-resources")
#RunWith(AspectJUnit4Runner.class)
public class HelloTest {
#Test
public void getLiveGreeting() {
String expected = "Hello FRIEND!";
HelloService helloService = new HelloService();
String greeting = helloService.sayHello("friend");
Assert.assertEquals(expected, greeting);
}
}
Related
I'm trying to create a controller test with #WebMvcTest, and as I understand, when I put #WebMvcTest(ClientController.class) annotation of the test class it should not create a whole lot of beans, but just ones that this controller requires.
I'm mocking the bean this controller requires with #MockBean, but somehow it fails with an exception that there's 'No qualifying bean' of another service that does not required by this controller but by another.
So this test is failing:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = ClientController.class)
public class ClientControllerTest {
#MockBean
ClientService clientService;
#Test
public void getClient() {
assertEquals(1,1);
}
}
I've created an empty Spring Boot project of the same version (2.0.1) and tried to create test over there. It worked perfectly.
So my problem might be because of the dependencies that my project has many, but maybe there's some common practice where to look in this situation? What can mess #WebMvcTest logic?
I've found a workaround. Not to use #WebMvcTest and #MockBean, but to create everything by hand:
//#WebMvcTest(ClientController.class)
#RunWith(SpringRunner.class)
public class ClientControllerTest {
private MockMvc mockMvc;
#Mock
ClientService clientService;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(
new ClientController(clientService)
).build();
}
works with Spring 1.4.X and with Spring Boot 2.X (had different exception there and there), but still doesn't explain why #WebMvcTest doesn't work
I have spring boot application which works well, but when I started to work on integration tests, I discovered, that there is cyclic dependency in project:
#Service
public class CrowdManagerSyncService {
private final CrowdManagerSyncScheduler crowdManagerSyncScheduler;
#Autowired
public CrowdManagerSyncService(CrowdManagerSyncScheduler crowdManagerSyncScheduler) {
this.crowdManagerSyncScheduler = Objects.requireNonNull(crowdManagerSyncScheduler);
}
}
And
#Component
public class CrowdManagerSyncScheduler {
#Autowired
private CrowdManagerSyncService crowdManagerSyncService;
}
It is not my code and I am not ready to rewrite it right now. But it works perfectly well in production. In my integration test
#RunWith(SpringRunner.class)
#WebMvcTest(UserController.class)
#WithMockUser(roles={"ADMIN"})
#ContextConfiguration(classes = {AdminConsoleApplication.class, DataSourceAutoConfiguration.class,
MockMvcAutoConfiguration.class, MockMvcWebDriverAutoConfiguration.class})
public class UserControllerTest {
#Autowired
private MockMvcHtmlUnitDriverBuilder builder;
private WebDriver webDriver;
#Before
public void setUp() throws Exception {
webDriver = builder.build();
}
}
I catch exception:
Error creating bean with name 'crowdManagerSyncService': Requested bean is currently in creation: Is there an unresolvable circular reference?
So, my question is: how to omit this problem in testing without removing that awful circular dependency? It works well in production, so pretty sure there is some way to start test context without code change.
#WebMvcTest is not suitable for "proper" integration tests.
From the api docs:
Can be used when a test focuses only on Spring MVC components.
However, you're then using #ContextConfiguration to essentially add your whole application to the test.
Remove the #ContextConfiguration and instead autowire a #MockBean CrowdManagerSyncService into your test.
This creates a mock version of CrowdManagerSyncService and injects it into the UserController in the test application context.
#RunWith(SpringRunner.class)
#WebMvcTest(UserController.class)
#WithMockUser(roles={"ADMIN"})
public class UserControllerTest {
#Autowired
private MockMvcHtmlUnitDriverBuilder builder;
#MockBean
private CrowdManagerSyncService service;
private WebDriver webDriver;
#Before
public void setUp() throws Exception {
webDriver = builder.build();
}
#Test
public void shouldWork() {
when(service.doStuff())
.thenReturn("Hello"); // regular Mockito mocking
}
}
This is appropriate if you're just trying to test the UserController and sidesteps the circular dependency problem because there's no instantiation of a "real" CrowdManagerSyncService anywhere.
You can also replace #WebMvcTest and #ContextConfiguration with both #SpringBootTest (which bootstraps the application just like production) and #AutoConfigureMockMvc (which replaces the real HTTP stuff with MockMvc).
//Update
After viewing helpful comments, I realize the problem should then be, how to unit test method using values read from properties by #Value .
//
I am working on this issue for days, I am writing unit test for a serviceClass.The serviceClass is like below :
import ...
#Component
public class ServiceClass implements ServiceInterface {
#Value("${data.layer.url}")
private String dataLayerUrl;
#Autowired
private RestTemplate restTemplate
public void dummy(){
restTemplate.postForObject(dataLayerUrl + "/" + ... , ...);
}
}
And CONFIG_DIR is already defined in application configuration file.
I have a SomeConfig class defining beans as below. (...src/main/java/com.app/configuration/SomeConfig)
#Configuration
#ComponentScan(basePackages = {"..."})
#PropertySource(value = "file:${CONFIG_DIR}/app.properties")
public class SomeConfig{
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
...
return restTemplate;
}
}
My test class is as below:
Import ...
#Profile("test")
public class ServiceClassTest extends AbstractTest {
#Value("${data.layer.url}")
private String dataLayerUrl;
#InjectMocks
private ServiceClass ServiceClass;
#Mock
RestTemplate restTemplate;
#Before
public void initializeMockito() {
MockitoAnnotations.initMocks(this);
}
#Test
public void dummyTest(){
when(restTemplate.postForObject(dataLayerUrl + "/" + ..., ...)).thenReturn(...);
serviceClass.dummy();
assertEquals(...);
verify(restTemplate).postForObject(...);
}
}
And then my AbstractTest as below :
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("test")
#SpringApplicationConfiguration(classes = Application.class)
#ContextConfiguration(classes = {TestConfiguration.class})
#ComponentScan(basePackages = ...)
public abstract class AbstractTest {
protected Logger logger = LoggerFactory.getLogger(this.getClass());
}
And I also have .../src/test/resources/application-test.properties defined as below
#Datalayer properties
data.layer.url=http://camel-dev-01.xxx.com:5001
This is the same as defined in application.properties(which locates outside of project in CONFIG_DIR.
The logic of testing is just to make sure when you call dummy method of serviceClass, the postForObject method of restTemplate is called exactly once.
But when doing it this way, I am facing with 2 problems.
when I run test class in debug mode, I found
in ServiceClassTest. dataLayerUrl = "$data.layer.url"
in ServiceClass. dataLayerUrl = null
I researched around and be able to solve problem one by following this link
https://gist.github.com/danlangford/3418696
But this is not an ideal way to do this, since by default spring should be able to read properties from application-test.properties.
And I never figured out what caused the second issue and how to solve it.
I think this would be a common issue when writing unit test on class which read properties from .properties file using $Value annotation. Any comments or suggestions would be very much appreciated.
The key point hear as said M. Deinum is that you use a mix of Spring bean and Mock Object that in this case aren't Spring bean and for this reason can't benefit of the feature of Spring Container such as the injection of the properties.
In particular you should use the spring test abstraction as a "integration test" istruments. With this words I intended that you should use this abstraction, for test the correct configuration, behavior and so on fo your bean in the spring contex. However if you use Stub or mock object you actually exit, of a smal part probably, by the management of spring and the your test don't make sense. Using stub or mock the your test become a Unit test in sense that it will be a test the your bean and functionality in isolation infact you have mock or stub the dependency of your object.
I hope that this reflection could be help you
I am glad to know there is no way to read values from properties by #Value inside a mock obj.
But still my problem is that I want to unit test my dummy method in ServiceClass. Put it another way, as long as I could unit test this method, I don't care whether #Value works or not.
Here is my solution of test method
#Profile("test")
public class ServiceClassTest extends AbstractTest {
#Value("${data.layer.url}")
private String dataLayerUrl;
#InjectMocks
private ServiceClass ServiceClass;
#Mock
RestTemplate restTemplate;
#Before
public void initializeMockito() {
MockitoAnnotations.initMocks(this);
}
#Test
public void dummyTest(){
when(restTemplate.postForObject(anyString() , eq(), eq() )).thenReturn(...);
serviceClass.dummy();
assertEquals(...);
verify(restTemplate).postForObject(anyString(), eq(), eq());
}
By using anyString, I don't rely on what value is read from properties, since I only want to test whether dummy method call restTemplate's postForObject method properly.
You need to add PropertySourcesPlaceholderConfigurer to your test configuration in order to populate properties annotated with #Value annotation. Spring Boot adds it to configuration, but since your test is running without Spring Boot you have to declare it. For more details seehere .
in place of #InjectMocks you can write #Autowired and I thing you can use both annotation like that
Case 1
#InjectMocks
private ServiceClass ServiceClass;
case 2
#Autowired
#InjectMocks
private ServiceClass ServiceClass;
I have same issue but after discussion my senior I have find above like solutions
We have many Controllers in our system, and many Spring Data repositories.
I would like to write tests for my controllers that run through my MVC context.
However, it seems pretty cumbersome, and just not right, to have to, by hand, mock every service and repository in my system, so that I can test the controllers
e.g.
FooControllerTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextHierarchy(value = {
#ContextConfiguration(classes = { MockServices.class }),
#ContextConfiguration({ "classpath:/META-INF/spring/mvc-servlet-context.xml" }),
})
public class FooControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mvc;
#Autowired
private FooRepository fooRepository;
#Autowired
private FooService fooService;
#Before
public void setUp() throws Exception {
mvc = webAppContextSetup(wac).build();
}
#Test
public final void list() {
when(fooRepository.findAll()).thenReturn(...);
mvc.perform(get("/foo"))...
}
#Test
public final void create() {
Foo fixture = ...
when(fooService.create(fixture)).thenReturn(...);
mvc.perform(post("/foo"))...
}
}
MockServices.java
#Configuration
public class MockServices {
#Bean
public FooRespository fooRepositiory() {
return Mockito.mock(FooRespository.class);
}
#Bean
public FooService fooService() {
return Mockito.mock(FooService.class);
}
//even though we are "only" testing FooController, we still need to mock BarController's dependencies, because BarController is loaded by the web app context.
#Bean
public BarService barService() {
return Mockito.mock(FooService.class);
}
//many more "mocks"
}
I do not really want to use standaloneSetup() (want to use the production configuration, eg conversion services, error handlers, etc)
is this just the price I have to pay for writing controller tests so far down the line?
Seems there should be something like mock every class annotated with #Service or mock every interface that extends JpaRepository
An MVC Controller is implemented normally like a glue code that integrates the Model with the View. For example, when invoking an EJB from the Controller and then updating the View model.
So, a Controller test may have sence when indeed you mock all your dependencies and verify that this integration or "glue code" is working as expected. In general, if an integration test implies too many components, maybe a modularization of the whole sut may be necessary for the system to be actually testable.
Anyway, if you find integration test laborious, maybe you can try to get the most coverage for each standalone component and let functional tests get the Controller coverage.
I'm trying to replace an #Autowired object with a Mockito mock object. The usual way of doing this was with xml using Springockito:
<mockito:mock id="SomeMock" class="com.package.MockInterface" />
Currently I'm trying to move over to using Spring's JavaConfig to do the job. All of a sudden the Java expressions are a whole lot more verbose than xml:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class MyTestClass {
#Configuration
static class Config {
#Bean
public MockInterface somethingSpecial() {
return Mockito.mock(MockInterface.class);
}
}
#Autowired MockInterface mockObj;
// test code
}
I discovered a library called Springockito-annotations, which allows you to do the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=SpringockitoContextLoader.class)
public class MyTestClass {
#Autowired #ReplaceWithMock MockInterface mockObj;
// test code
}
Clearly, a whole lot prettier :) The only problem is that this context loader doesn't allow me to use #Configuration and JavaConfig for other beans (if I do, Spring complains that there are no candidates that match those autowired fields).
Do you guys know of a way to get Spring's JavaConfig and Springockito-annotations to play nice? Alternatively, is there another shorthand for creating mocks?
As a nice bonus, using Springockito and xml config, I was able to mock out concrete classes without providing autowiring candidates to its dependencies (if it had any). Is this not possible without xml?
Moving away from the now unmaintained (as of this writing) Spingockito-annotations and to Mockito, we have a way of doing this very simply:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration
public class MyTestClass {
#Mock MockInterface mockObj;
// test code
}
If you're using a real object, but would like to mock a dependency within it, for instance testing a service layer with DAO:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration
public class MyTestClass {
#InjectMocks RealService;
#Mock MockDAO mockDAO;
// test code
}
Finally, this can also be applied to Spring-boot, but using annotation initialization within setUp() until multiple class runners are supported:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyMainSpringBootClass.class)
public class MyTestClass {
#InjectMocks RealService;
#Mock MockDAO mockDAO;
#Before
public final void setUp() throws Exception{
MockitoAnnotations.initMocks(this);
}
// test code
}
Outdated and deprecated!
Read about mocking and spying in Spring Boot 1.4
Please read also #ethesx answer,
Springockito is unmaintaned
Old answer
This is possible now to mock Spring application without any XML file with Springockito-annotations.. This solution works also with Spring Boot.
import static org.mockito.BDDMockito.*;
import org.kubek2k.springockito.annotations.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class,
loader = SpringockitoAnnotatedContextLoader.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class MainControllerTest {
#Autowired
MainController mainController;
#Autowired
#ReplaceWithMock
FooService fooService;
#Test
public void shouldGetBar() {
//given
given(fooService.result("foo")).willReturn("bar");
//when
Bar bar build = fooService.getBar("foo");
//then
assertThat(bar).isNotNull();
}
}
Dependencies: org.kubek2k:springockito-annotations:1.0.9
It appears that SpringockitoContextLoader extends GenericXmlContextLoader which is described as:
Concrete implementation of AbstractGenericContextLoader that reads bean definitions from XML resources.
So you are limited to xml bean definitions at the moment.
You could write your own context loader, taking relevant parts from the SpringockitoContextLoader class. Take a look here to get started, perhaps you could extend AnnotationConfigContextLoader for example?