I have an issue while testing REST requests. On my application I have an interceptor that checks for token validity before allowing the requests. However for my integration tests I would like to bypass the check. In other words I'd like either to shunt the interceptor or to mock it to always return true.
Here is my simplified code:
#Component
public class RequestInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String token = request.getHeader("Authorization");
if (token != null) {
return true;
} else {
return false;
}
}
}
#Configuration
public class RequestInterceptorAppConfig implements WebMvcConfigurer {
#Autowired
RequestInterceptor requestInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestInterceptor).addPathPatterns("/**");
}
}
and the tests:
#SpringBootTest(classes = AppjhipsterApp.class)
#AutoConfigureMockMvc
#WithMockUser
public class DocumentResourceIT {
#Autowired
private DocumentRepository documentRepository;
#Autowired
private MockMvc restDocumentMockMvc;
private Document document;
public static Document createEntity() {
Document document = new Document()
.nom(DEFAULT_NOM)
.emplacement(DEFAULT_EMPLACEMENT)
.typeDocument(DEFAULT_TYPE_DOCUMENT);
return document;
}
#BeforeEach
public void initTest() {
document = createEntity();
}
#Test
#Transactional
public void createDocument() throws Exception {
int databaseSizeBeforeCreate = documentRepository.findAll().size();
// Create the Document
restDocumentMockMvc.perform(post("/api/documents")
.contentType(MediaType.APPLICATION_JSON)
.content(TestUtil.convertObjectToJsonBytes(document)))
.andExpect(status().isCreated());
}
}
When running the tests it always go through the interceptor and gets rejected since I have no valid token. My code here is simplified, I can not get a valid token for testing and so I really need to skip the interceptor.
Thanks for your help
To mock it (in an integration test):
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
// non-static imports
#SpringBootTest
// other stuff
class IntegrationTest {
#MockBean
RequestInterceptor interceptor;
// other stuff
#BeforeEach
void initTest() {
when(interceptor.preHandle(any(), any(), any())).thenReturn(true);
// other stuff
}
// tests
}
What #BeforeEach and #SpringBootTest do, you know; Mockito's any() just says "regardless of argument"; for #MockBean and Mockito's when-then, the Javadoc is good enough that I feel no need to add information.
I would solve this by using a profile on the interceptor. In your test you dont run with the profile (bean is not injected). In your production or which ever env you need it you run with the new profile.
of course you need to change the usage a bit. This should work:
#Configuration
public class RequestInterceptorAppConfig implements WebMvcConfigurer {
#Autowired
Collection<RequestInterceptor> requestInterceptors;
#Override
public void addInterceptors(InterceptorRegistry registry) {
requestInterceptors.forEach(interceptor -> registry.addInterceptor(interceptor).addPathPatterns("/**");
}
}
Related
I am doing some web unit testing with Spring. is there a way I can mock the instance of MyProcessor which get set in #PostConstruct? I tried with #MockBean but it gets set as null and getting null pointer exception?
I am using a factory to instantiate MyProcessor based on some boolean flag. But if there is a different approach altogether that would make my test cleaner, I am open to ideas.
Please Note I am using Junit 5.
public class Controller {
private final AProcessorFactory factory;
#Value("${tt.f.processor.async}")
private boolean isAsync;
private MyProcessor myProcessor;
public Controller(final AProcessorFactory factory) {
this.factory= factory;
}
#PostConstruct
public void init() {
myProcessor = factory.getInstance(isAsync);
}
}
#WebMvcTest(Controller.class)
public class ControllerTest{
#MockBean
private MyProcessor processor;
#MockBean
private AProcessorFactory factory;
#Autowired
private MockMvc mockMvc;
#Test
public void test() throws Exception {
when(processor.process(any(Request.class), any(String.class)))
.thenReturn(new TestResponse("123", SUCCESS, "", ""));
}
It looks like your myProcessor is actually built by your AProcessorFactory in a PostConstruct init() method.
You'll want to provide a behavior for your AProcessorFactory mock.
First, you would probably want to set up your myProcessor in your constructor as the #PostConstruct init() method has no special context loading logic with it.
public class Controller {
#Value("${tt.f.processor.async}")
private boolean isAsync;
private MyProcessor myProcessor;
public Controller(final AProcessorFactory factory) {
this.myProcessor = factory.getInstance(isAsync);
}
}
You can specify this in a #Before step in your test.
#MockBean
private AProcessorFactory factory;
#Before
public void setUp() {
when(processor.process(any(Request.class), any(String.class)))
.thenReturn(new TestResponse("123", SUCCESS, "", ""));
when(factory.getInstance(any())).thenReturn(processor)
}
Spring will register your AProcessorFactory mock that has been loaded with the appropriate behaviors.
I'm having an issue while testing Rest request.
However test is being intercepted by one of my interceptors.
In short I want to mock interceptors behaviour rather than calling actual implementation.
For simplicity purposes to visualise the issue here is a simply Get request
#Controller
public class HiController {
#ResponseBody
#GetMapping(value = "/hi", produces = {"application/json"})
public String hi() {
return "hi";
}
}
with simple interceptor and config file
#Component
public class LoggingInterceptor implements HandlerInterceptor {
#Override
public final boolean preHandle(HttpServletRequest request, final HttpServletResponse response, final Object handler) {
System.out.println("Logging token interceptor");
return true;
}
}
#Component
public class InterceptorAppConfig implements WebMvcConfigurer {
#Autowired
private LoggingInterceptor loggingTokenInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingTokenInterceptor);
}
}
the purpose of test was to call rest endpoint and to mock interceptor
#RunWith(SpringRunner.class)
#WebMvcTest(HiController.class)
public class HiControllerTest extends MvcTest {
#Autowired
private MockMvc mockMvc;
#MockBean
RestTemplate restTemplate;
#Mock
private HiController controller;
private static final String AUTHENTICATE_USER = "/hi";
#Test
public void mandatoryFieldMissing() throws Exception {
this.mockMvc.perform(get(AUTHENTICATE_USER))
.andExpect(content().string(containsString("hi")))
.andExpect(status().isOk());
}
}
#RunWith(SpringRunner.class)
public abstract class MvcTest {
#MockBean
LoggingInterceptor loggingTokenInterceptor;
}
When I run this test ^ LoggingInterceptor is still being called.
How can I configure tests to have mock interceptors?
You are using #MockBean. From the documentation,
If a bean, compatible with the declared class exists in the context, it replaces it by the mock. If it is not the case, it adds the mock in the context as a bean.
The mocked bean does not have behavior. Realize that by mocking the bean, the onus is on you to define the behavior.
You can either disable mocking the bean(to allow for default behavior)
Mockito.doCallRealMethod().when(loggingTokenInterceptor).preHandle(Mockito.any(), Mockito.any(), Mockito.any());
Or you can define the behavior for the mocked bean yourself.
I was wondering what I am doing wrong when testing my aspect functionality. The aspect is working in production (passed testing by QA), but I am trying to get my integration unit test to pass. Here is my code:
#Aspect
#Component
public class MyAspect {
#Pointcut("execution(* com.example.dao.UsersDao(..)) && args(.., restrictions)")
protected void allUsersPointcut(List<String> restrictions) {
}
#Around("allUsersPointcut(restrictions)")
public Object applyUserRestrictions(final ProceedingJoinPoint pjp, List<String> restrictions) throws Throwable {
String restrict = "Jack";
restrictions.add(restrict);
return pjp.proceed();
}
My DAO method just returns a list of all users, but when the aspect is used it restricts what users are shown.
#Repository
UsersDaoImpl implements UsersDao {
...
}
And my UsersService:
#Service
public class UsersService implements UsersService {
#Autowired
protected UsersDAO usersDAO;
...
#Transactional(readOnly=true)
public List<String> findUsers(List<String> restrictions) {
return this.usersDAO.findUsers(restrictions);
}
}
In my unit test I am doing the following:
#RunWith(SpringJUnit4ClassRunner.class)
public class UserTest {
#Autowired
UsersService usersService;
#Test
public void testAspect() {
List<String> restrictions = null;
List<String> users = this.usersService.findUsers(restrictions);
Assert.notNull(users);
}
I've also added the xml confguration:
context:annotation-config></context:annotation-config>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<context:component-scan base-package="com.example.aspect" />
Can anyone advise what I am doing wrong?
From what I can see of your test, it ought to work - so you have some tweaking to do regarding classpath scanning, ensuring the test is using the expected configuration etc.
I recommend temporarily adding:
#Autowired
ApplicationContext context;
#Before
public void dumpBeans() {
System.out.println(context.getBeansOfType(UsersDao.class));
}
Or, more simply, System.out.println(usersDao.getClass()) in the test method.
You could also run your test in a debugger - add a breakpoint in your test class, and check what class usersDao is at runtime.
I searched SO and found bunch of other questions that looked similar but not exactly, so I'll ask another one.
I have Spring application and say I created custom aspect (looking for CatchMe annotation) to log exceptions in a specific way. I want to test the aspect by mocking the behavior of one of my Spring #Service class's method so it throws exception when it is called. Then in another method, annotated with my custom annotation #CatchMe, I call the first method. What I expect to happen is the exception to get logged. Unfortunatelly the exception is thrown but the aspect is not triggered. So how can I make the aspect to get triggered in this test using Mockito?
Note: I've checked those (plus a bunch more):
Unit testing Spring #Around AOP methods
Spring Aspect not triggered in unit test
Spring: cannot inject a mock into class annotated with the #Aspect annotation
but most of them are Controller related and not Service related and I want to test only the service.
The Test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {BeanConfig.class})
public class MyServiceTest {
#Autowired
#InjectMocks
private MyService service;
#Mock
private MyServiceDependency serviceDep;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(service, "serviceDep", serviceDep);
}
#Test
public void test() {
when(serviceDep.process()).thenAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
throw new Exception("Sample message.");
}
});
service.execute();
}
}
Services
#Service
public class MyService {
#Autowired
private MyServiceDependency serviceDep;
#CatchMe
public void execute() {
serviceDep.process();
}
}
#Service
public class MyServiceDependency {
public Object process() {
// may throw exception here
}
}
Configuration and Aspect
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages = {"com.example.services"})
public class BeanConfig { .. }
#Aspect
#Component
public class CatchMeAspect {
#Around("#annotation(CatchMe)")
public Object catchMe(final ProceedingJoinPoint pjp) throws Throwable {
try {
pjp.proceed();
} catch (Throwable t) {
// fency log
}
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface CatchMe {}
EDIT: The functionality works but I want to verify it with the test.
Actually it is working as expected, however you are running in a side effect of proxy based AOP, especially class based proxies in this case.
Currently you are setting the field on the proxy and not on the actual object inside the proxy. Which is what you actually want. To obtain the actual instance use AopTestUtils.getUltimateTargetObject and then use that in the ReflectionTestUtils.setField method.
#Autowired
#InjectMocks
private MyService service;
#Mock
private MyServiceDependency serviceDep;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
MyService serviceToInject = AopTestUtils.getUltimateTargetObject(service);
ReflectionTestUtils.setField(serviceToInject, "serviceDep", serviceDep);
}
However I think that approach is wrong, when you start messing around like this there is a better way. Simply use Spring to inject the mock. Create a specific #Configuration class for this test case. Make it a internal public static class and for the dependency add a mocked #Bean.
#Configuration
#Import(BeanConfig.class)
public static class TestBeanConfig {
#Bean
public MyServiceDependency myServiceDependency() {
return Mockito.mock(MyServiceDependency.class);
}
}
Now in your test class you can simply #Autowire both beans and not need to use reflection or whatever to set dependencies.
#RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
#Autowired
private MyService service;
#Autowired
private MyServiceDependency serviceDep;
#Test
public void test() {
when(serviceDep.process()).thenAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
throw new Exception("Sample message.");
}
});
service.execute();
}
}
Which will take care of the correct dependencies.
I had the same problem as #nyxz and this is intentional, see https://github.com/spring-projects/spring-boot/issues/7243.
Inspired by #M. Deinum following solution worked for me with Spring Boot 2.3.4.RELEASE and JUnit 5.
We will just provide a mocked bean without #MockedBean
#SpringBootTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class MyServiceTest {
#Autowired
private MyService service;
#Test
public void test() {
service.execute();
}
static class TestBeanConfig {
#Bean
#Primary
public MyServiceDependency myServiceDependency() {
MyServiceDependency myServiceDependency = Mockito.mock(MyServiceDependency.class)
// Add behavior of mocked bean here
return myServiceDependency;
}
}
}
I have a problem I'm not able to solve. I have searched on the internet and on Stackoverflow but could not find how to solve the problem.
I want to test a Spring MVC Handler interceptor. This interceptor has a "session" scope bean as a dependency.
I tried to reduce the code as much as possible. Here is the code:
The src part :
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = { "..." })
public class SpringMvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry interceptorRegistry) {
interceptorRegistry.addInterceptor(initializeUserLanguageHandler());
}
#Bean
public InitializeUserLanguageHandler initializeUserLanguageHandler() {
return new InitializeUserLanguageHandler();
}
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public SessionBean sessionBean() {
return new SessionBean();
}
}
#Component
public class InitializeUserLanguageHandler extends AbstractHandlerInterceptor {
#Autowired
private SessionBean sessionBean;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (sessionBean.getLanguage() == null) {
sessionBean.setLanguage(getUserLanguage());
}
return true;
}
}
The test part:
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringMvcConfiguration.class)
public class BaseSpringMvcIntegrationTest {
#Resource
protected WebApplicationContext webApplicationContext;
protected MockMvc mockMvc;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
}
public class InitializeUserLanguageHandlerTest extends BaseSpringMvcIntegrationTest {
#Autowired
private SessionBean sessionBean;
#Autowired
private MockHttpSession mockHttpSession;
#Test
public void testLanguageIsInitializedOnlyOnce() throws Exception {
MockHttpSession mocksession = new MockHttpSession();
// It is null, this is because the interceptor has not been called yet
assertEquals(null, sessionBean.getLanguage());
// This line will call the interceptor and set language to "nl"
mockMvc.perform(get("/").session(mocksession).principal(getUser("nl")));
// It is null, but I expect it to be "nl"
assertEquals(null, sessionBean.getLanguage());
// Let's try again
mockMvc.perform(get("/").session(mocksession).principal(getUser("fr")));
// It is null, but I expect it to be "nl"
assertEquals(null, sessionBean.getLanguage());
}
}
You can see in the test class "InitializeUserLanguageHandlerTest" that I have some assertions.
The first time I call:
mockMvc.perform(get("/").session(mocksession).principal(getUser()));
The code in the interceptor is executed and language is set to "nl". Therefore, in my test, I would have expected that sessionBean.getLanguage() would return me "nl", but it is not. I don't understand why.
So I'm calling the perform again, the interceptor code is executed again, and calling sessionBean.getLanguage() returns "nl".
It seems I've two SessionBean instances, one in my test and the other in the source. But when I look at the SessionBean variable in Eclipse in Debug mode, they have the same ID.
If I change the "session" scope to "application" scope, it is working properly.
Can somebody help me ?
Thank you.
Here is one way to solve the problem, not sure it is the best though.
#ContextConfiguration(classes = {SpringMvcConfiguration.class, InitializeUserLanguageHandlerTest.BeanConfig.class})
public class InitializeUserLanguageHandlerTest extends BaseSpringMvcIntegrationTest {
#Configuration
public static class BeanConfig {
#Bean(name = "sessionBean")
public SessionBean sessionBean() {
return new SessionBean();
}
}
...
}