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?
Related
Spring AOP advices are not being applied for unit tests. Everything seems to be working fine during normal execution and integration tests, but are not applied while running unit tests.
Relatively new to Spring and battling with this issue for a while. Looks like some configuration issue. Tried with different types of runners but did not have any luck. Also tried to integrate with AspectJWeaver for compile time weaving but hit many compile issues across the legacy code base which I stepped back.
Unit test
#RunWith(SpringRunner.class)
#SpringBootTest
#EnableAspectJAutoProxy
public class UserServiceImpl
private UserServiceImpl userServiceSpy;
#Mock
private UserDao userDao;
#Mock
private MembershipDao membershipDao;
#Mock
private Service1 service1;
#Mock
private Service2 service2;
#Mock
private TroubleshootingLogService troubleshootingLogService;
#Before
public void setup() {
UserServiceImpl userService = new UserServiceImpl(userDao, membershipDao,service1, service2, <param1>, <param2>);
userServiceSpy = spy(userService)
// some other variables inits...
}
// All the unit tests.
Integration test
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#TestExecutionListeners(value = {FlywayTestExecutionListener.class}, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
#FlywayTest
#ActiveProfiles("local")
public class UserServiceIntegrationTest {
#ClassRule
public static final WireMockClassRule wireMockRule = new WireMockClassRule(wireMockConfig().dynamicPort());
#Autowire
private UserDao userDao;
#Autowire
private MembershipDao membershipDao;
#Autowire
private Service1 service1;
#Autowire
private Service2 service2;
#Before
public void init(){
//clean up persisted test states
}
// All integration tests
}
Aspect
#Aspect
#Component
#Order(1)
public class UserExceptionLoggingAdvisor extends AbstractExceptionLoggingAdvisor {
private static final Logger LOGGER = LoggerFactory.getLogger(UserExceptionLoggingAdvisor.class);
#Around("#annotation(LogException) && args(directoryId, userId, userToUpdate)")
public Object handleException(ProceedingJoinPoint joinPoint, String directoryId, String userId, ExternalUser userToUpdate) throws Throwable {
LOGGER.debug("Advising execution to handle possible ScimException");
}
When we have a breakpoint on the Aspect class, at private static final Logger LOGGER = LoggerFactory.getLogger(UserExceptionLoggingAdvisor.class); line, the unit test breaks. But it does not break at the actual #Around advice for unit tests while it does for integration tests.
Can anyone advise me on how to fix this issue.
It's not a "configuration issue"
Since you havn't disclosed how you are calling your unit tests and none of the classes. My guess is that it isn't triggered since you have expressed the fully qualified name to your LogException annotation.
it should be my.full.packagename.logexception
My advice is to keep your "unittests" as simple as possible and give up that idea of getting Aspects to work in your unit test.
Too many mocks is never a good thing,
Testing on the Toilet: Don’t Overuse Mocks
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
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.
I have an issue that I would have thought I could resolve by now...
I'm writing a few simple tests to hit a couple services...
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class EndpointTests {
private static Logger log = Logger.getLogger(ApplicationController.class.getName());
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Mock
private ApplicationController applicationController = new ApplicationController();
static {
System.setProperty("audit.enabled", "false");
}
#BeforeClass
public static void setupProperties() {
System.setProperty("audit.enabled", "false");
}
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Test
public void contextLoads() {}
#Test
public void testGetApplications() throws Exception {
mockMvc.perform(get("/applications/")).andExpect(status().isOk())
.andDo(print())
.andExpect(content().contentType(TestUtils.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.[0].name",is("XYZ")));
}
Long story short, I need this property disabled when running tests. I've tried setting the property in a static initializer and in #BeforeClass as I've seen on other posts but when it goes into the actual method, it's still its default 'enabled' value and the tests fail. I'm not using XML configuration so would prefer a code/annotation solution.
Any suggestions on another way I can fix this? Thanks.
UPDATE
Seems like every time my integration test runs:
#Test
public void testGetApplications() throws Exception {
mockMvc.perform(get("/applications/")).andExpect(status().isOk())
.andDo(print())
.andExpect(content().contentType(TestUtils.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.[0].name",is("paasport-services")));
}
It executes my #Configuration classes on the call to mockMvc.perform...
#Configuration
public class AppConfiguration
...
...
So setting the property value in my test class does no good.
Is there any way to get in between and set this one property for my tests? I don't want to really create a separate test application context as it's just one property and everything has been working well for me up to this point.
Thanks.
I'm sure there's a much more elegant solution but I simply set a new system property in #BeforeClass on my test class.
My audit is handled by an aspect and I simply check that property I set only in my test class. If it's set to true, the advice doesn't execute.
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();
}