SpringBoot: WebMvcTest with authorization - spring

I have a basic SpringBoot app. using Spring Initializer, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file.
I have this test:
#RunWith(SpringRunner.class)
#WebAppConfiguration
#WebMvcTest
public class MockMvcTests {
// Pull in the application context created by #ContextConfiguration
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#MockBean
private I18NService i18NService;
#MockBean
private EmailService emailService;
#MockBean
private PasswordResetTokenService passwordResetTokenService;
#MockBean
private UserService userService;
#MockBean
private CompanyService companyService;
#MockBean
private UserSecurityService userSecurityService;
#Before
public void setup() {
// Setup MockMVC to use our Spring Configuration
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
/**
*
* #throws Exception
* If anything fails.
*/
#Test
public void getDeviceEventsTest() throws Exception {
this.mockMvc
.perform(get("/deviceevent/list") //
.accept(MediaType.parseMediaType("text/html;charset=UTF-8")))
.andExpect(status().isOk()) //
.andExpect(model().size(1)); //
}
But running the Test I got this Exception
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "#authorization.expression('hasRole(''ROLE_ADMIN'')')" (tdk/common/menu:62)
Is there a way to bypass the authorization ?

I see first possible problem in strange expression in
hasRole(''ROLE_ADMIN''), looks like two single quotes instead of double quotes here, but maybe it looks strange just in log.
Anyway, to bypass auth mechanism for tests you need to disable Spring Security in test mode, there are some working ways described here
I usually use this way with configuration and profiles setting, because it takes more flexibility then other described ways.

Related

Spring MockMVC Test weird behaviour. single vs "all" execution

I am currently experiencing a weird issue regarding Spring's MockMvc in combination with Spring Security in JUnit Tests.
When I run a whole class of tests, everything works fine and the tests are passing.
But when I run All Tests in the Project one test is always failing in that certain class and it doesn't matter if I remove the test method failing, then another one fails in the same class.
#SpringBootTest
#ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
class AuthenticationApiTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
private static ObjectMapper objectMapper;
#BeforeAll
static void beforeAll() {
objectMapper = new ObjectMapper();
}
#BeforeEach
void setUp(RestDocumentationContextProvider documentation) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext)
.apply(documentationConfiguration(documentation)
.operationPreprocessors()
.withRequestDefaults(prettyPrint(),removeHeaders("Content-Length","Host","Pragma","X-XSS-Protection","Expires","X-Frame-Options","X-Content-Type-Options","Cache-Control"))
.withResponseDefaults(prettyPrint(),removeHeaders("Content-Length","Host","Pragma","X-XSS-Protection","Expires","X-Frame-Options","X-Content-Type-Options","Cache-Control")))
.apply(springSecurity())
.build();
}
The test which fails:
#Test
public void testSignUpFail() throws Exception {
SignUpBody signUpBody = new SignUpBody();
signUpBody.setPassword("demo1234");
signUpBody.setFirstname("first");
signUpBody.setLastname("lastn");
this.mockMvc.perform(post("/auth/user/signup")
.header(HttpHeaders.CONTENT_TYPE,"application/json")
.content(objectMapper.writeValueAsString(signUpBody))
).andExpect(status().is(400))
.andDo(document("user-signup-fail"));
this.mockMvc.perform(post("/auth/vendor/signup")
.header(HttpHeaders.CONTENT_TYPE,"application/json")
.content(objectMapper.writeValueAsString(signUpBody))
).andExpect(status().is(400))
.andDo(document("vendor-signup-fail"));
}
Any suggestions on how to solve this weird issue?

Async RestController endpoints :: Async result for handler was not set during the specified timeToWait=0

We have endpoints of the form:
#GetMapping("/myurl")
public Callable<String> getARandomString(#RequestParam int a) {
The application is configured with just one thread (that's why we use Callables to handle this). However, in order to create the tests for this, we do the setup as follows:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {MyApplication.class, MyController.class})
#ActiveProfiles("test")
public class MyControllerTest {
#MockBean
private MyService myService;
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void before() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(wac)
.build();
when(myServices.isOk()).thenReturn(true);
}
#Test
public void given_when_then() throws Exception {
MvcResult mvcResult = mockMvc.perform(get("/myurl")
.param("a", 1))
.andExpect(request().asyncStarted())
.andReturn();
mvcResult.getAsyncResult();
mockMvc
.perform(asyncDispatch(mvcResult))
.andExpect(status().isOk());
}
1 test at the time works perfect. On the contrary, when having more than 1 test an exception as the following is thrown:
java.lang.IllegalStateException: Async result for handler [public java.util.concurrent.Callable<String> com.antmordel.MyController. getARandomString(int)] was not set during the specified timeToWait=0
I reckon that it is something about the timeouts. Have anybody had this issue?
I had the exactly same problem. 01 test = OK, multiple tests in 01 run = FAIL.
Fixed by telling the TestRunner to consider that the current test makes the context "dirty".
Add
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) to your test class
See this question Reload Spring application context after every test

Test spring boot app with servlet parameters in application.yml

In application.yml there is the following parameter:
server:
context-parameters:
appCode: MYAPPCODE
This parameter is read by a third-party library. When running on embedded Tomcat, the parameter is available in the ServletContext. However, when running a test on SpringRunner, the ServletContext has no parameter.
Here is the relevant part of the test class.
#RunWith(SpringRunner.class)
#SpringBootTest
#WebAppConfiguration
#AutoConfigureMockMvc
public class RestControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
private void create() {
String content = createContent();
mockMvc.perform(post("/api/entity/create").content(content))
.andExpect(jsonPath("id").isNumber());
}
}
How can I configure the test class so that the mocked ServletContext is set with the context parameters from the application.yml?
Currently, to overcome the problem, I did the following workaround in the test class.
#Autowired
private ServletContext servletContext;
#Autowired
private ServerProperties serverProperties;
#Before
public void setup() throws Exception {
Map<String, String> params = serverProperties.getContextParameters();
new InitParameterConfiguringServletContextInitializer(params)
.onStartup(servletContext);
}
How can I configure the test class so that the mocked ServletContext
is set with the context parameters from the application.yml?
AFAIK, that is currently not possible when using WebEnvironment.MOCK (i.e., the default mode for #SpringBootTest) since the MockServletContext used by the WebApplicationContext configured by Spring Boot is plain vanilla (i.e., not populated with external values).
Thus, your "workaround" is the only solution.

Spring Boot + Mockito - MockMvc lacking methods

i have this simple test:
#RunWith(SpringRunner.class)
#WebMvcTest(MainController.class)
public class MainControllerTest extends ControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private Storage storage;
#MockBean
private PersonListMarshaller marshaller;
#Test
public void getTest() throws Exception{
mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(view().name("index"));
}
#Test
public void postTest() throws Exception{
}
}
in the postTest() method i wanna call:
mvc.perform(post("/")).param(...); I have a problem with the "param()" part, because intelliJ Idea doesnt recognize that method. I've searched through the documentation and also doesn't found it. I've seen people using it in various spring-related sites though (and few other methods that are unavailable for me). Why can't i use it?
The call to param needs to be off of post. More specifically, post returns MockHttpServletRequestBuilder which has the param method. Should look like
mvc.perform(post("/").param("", ""))
.andExpect(...)

Initialising MockMvc through Mockito

I'm having some trouble trying to get MockMvc to run with Mockito. This is what I have so far:
public class mockito {
MockMvc mockMvc;
#InjectMocks
EntryController controller;
#Mock
DAO data_access_object;
SmokeEvent evt = new SmokeEvent(18);
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = standaloneSetup(controller).setMessageConverters(new MappingJackson2HttpMessageConverter()).build();
//Exception thrown at above line (line 44) when the build method is called
}
#Test
public void viewRendersCorrectly() throws Exception
{
/**
* This test method is supposed to check to see if the controller renders
* correctly
*/
when(data_access_object.getEvent(any(Integer.class))).thenReturn(evt);
}
}
The following is the stack trace it throws in Testing mode. I should also state that I've been working through the Spring REST tutorial over at spring.io
java.lang.ExceptionInInitializerError: null
at java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:1499)
at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1322)
at java.util.ResourceBundle.getBundle(ResourceBundle.java:721)
at javax.servlet.GenericServlet.<clinit>(GenericServlet.java:95)
at org.springframework.test.web.servlet.MockMvcBuilderSupport.createMockMvc(MockMvcBuilderSupport.java:50)
at org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder.build(DefaultMockMvcBuilder.java:193)
at test.mockito.setup(mockito.java:44)
Any help is appreciated.
Thanks
Since you are running a standalone test, you need to create an instance of EntryController for standaloneSetup() method. As it is, you're passing standaloneSetup a null reference.
The other option is to run a semi-integrated test with MockMvcBuilders.webAppContextSetup and an appropriate WebApplicationContext.
for example:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes= {yourconfigurationClasses.class})
public class ... {
#Inject
protected WebApplicationContext webApplicationContext;
#Before
public void beforeSetUp() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}

Resources