Could 'Spring RESTful' + 'Java Config' + 'Tests' be used without Spring MVC? - java

Can't make tests working with such a configuration.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
#TestPropertySource(value="classpath:app-test.properties")
public class LoginTest {
#Autowired
private WebApplicationContext context;
MockMvc mockMvc;
#InjectMocks
private AuthController authController;
#Test
public void testOk() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
mockMvc.perform(get("/api/auth")
.with(httpBasic("u", "p")))
.andExpect(status().isOk());
}
}
Getting error:
java.lang.IllegalStateException: springSecurityFilterChain cannot be null. Ensure a Bean with the name springSecurityFilterChain implementing Filter is present or inject the Filter to be used.

If you see your stacktrace , you can note that this is thrown at
SecurityMockMvcConfigurer.java (a spring lib class) and if your see the code at that line inside in this class , exception is raised from below code.
public static MockMvcConfigurer More ...springSecurity(Filter springSecurityFilterChain) {
Assert.notNull(springSecurityFilterChain,
"springSecurityFilterChain cannot be null");
return new SecurityMockMvcConfigurer(springSecurityFilterChain);
}
which means one bean springSecurityFilterChain is needed and that is how SecurityMockMvcConfigurer is build.
Thus you need to provide this autowired bean,
If you already have this bean , Then make it available
#Autowired
FilterChainProxy springSecurityFilterChain;
#Test
public void testOk() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(SecurityMockMvcConfigurers.springSecurity(springSecurityFilterChain))
//.setCustomArgumentResolvers(new AuthenticationPrincipalArgumentResolver()) // if you dont have that bean then try with this line and delete above line.
.build();
mockMvc.perform(get("/api/auth")
.with(httpBasic("u", "p")))
.andExpect(status().isOk());
}

Related

Spring MVC Testing with Mock

I got this unit test code to test my spring mvc view, its look like failing to initialize the service.
#AutoConfigureMockMvc
#ContextConfiguration(classes = { TestAppContext.class })
#WebMvcTest
#Transactional
class BillEntryControllerTest {
#Autowired
private BillEntryService billEntryService;
#Autowired
private MockMvc mockMvc;
#BeforeEach
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new BillEntryController())
.build();
}
#Test
public void checkUpdateBill() throws Exception {
billEntryService = Mockito.mock(BillEntryServiceImpl.class);
doNothing().when(billEntryService).addOrUpdateBill(any(BillEntry.class));
this.mockMvc
.perform(MockMvcRequestBuilders.post("/bill-entry/saveBillEntry").accept(MediaType.TEXT_HTML)
.param("amount", "10.0")
.param("owner", "User")
.param("property", "Prop")
.param("receiptNumber", "ABC")
.param("accountName", "AC")
.param("billerName", "BN")
.param("datePaid", "20/10/2022")
.param("dateDue", "20/10/2022"))
.andExpect(model().errorCount(0)).andExpect(status().isOk());
}
}
And getting following error
Caused by: java.lang.NullPointerException: Cannot invoke "org.de.service.BillEntryService.addOrUpdateBill(org.de.model.BillEntry)" because "this.billEntryService" is null
at org.de.controller.BillEntryController.saveBillEntry(BillEntryController.java:157)
#PostMapping("/saveBillEntry")
public String saveBillEntry(Model model, #Valid #ModelAttribute("billEntry") BillEntryFormDto dto,
BindingResult theBindingResult) {
BillEntry billEntry = new BillEntry();
if (dto.getBillId()!=null && !dto.getBillId().isEmpty()) {
logger.debug("biller id " + dto.getBillId());
billEntry = billEntryService.getBillEntryById(Integer.parseInt(dto.getBillId()));
}
if (theBindingResult.hasErrors()) {
logger.error("has errors ");
getDefault(model);
return "bill-entry-form";
}
//updating the form attributes
billEntry.setAccountName(dto.getAccountName());
billEntry.setAmount(Double.parseDouble(dto.getAmount().replaceAll("[^\\d.]", "")));
billEntry.setBillerName(dto.getBillerName());
billEntry.setDateDue(FormatHelper.getDateFromString(dto.getDateDue()));
billEntry.setDatePaid(FormatHelper.getDateFromString(dto.getDatePaid()));
billEntry.setProperty(dto.getProperty());
billEntry.setReceiptNumber(dto.getReceiptNumber());
billEntry.setOwner(dto.getOwner());
logger.info("attempt to save/update bill entires " + billEntry.getBillId());
logger.debug("entry " + billEntry);
//failing at here (line 157)
billEntryService.addOrUpdateBill(billEntry);
return "redirect:"+ dto.getRedirect();
}
I try to mock the BillEntryService but that didn't help me ether. Any tips on how to fix it or what I'm doing wrong?
This is because your mocked billEntryService is not injected into the controller under test. As a result, that field is not initialized (null) in the controller during the test.
To fix that, the billEntryService must be annotated with #MockBean instead of #Autowired in the test. That will inject the mocked version of BillingEntryService into the controller. And you shouldn't initialize that mocked billEntryService using Mockito.mock(...) afterwards. The mockMvc shouldn't be initialized either, because you are using #WebMvcTest that already injects a proper mockMvc and you are autowiring it. So, the #BeforeEach method is also to be removed.
Given the above, the relevant part of the test will look like this:
#ContextConfiguration(classes = { TestAppContext.class })
#WebMvcTest
#Transactional // doesn't seem necessary here but I haven't touched this
class BillEntryControllerTest {
#MockBean
private BillEntryService billEntryService;
#Autowired
private MockMvc mockMvc;
#Test
public void checkUpdateBill() throws Exception {
doNothing().when(billEntryService).addOrUpdateBill(any(BillEntry.class));
mockMvc.perform...
}
Also, you can find this answer quite helpful.

Error running JUnitWeb Test on Method setup

I have a Spring application,
and I've created this test:
#RunWith(SpringRunner.class)
#SpringJUnitWebConfig(locations = {
"classpath:testDatabaseContext.xml",
"classpath:testServicesContext.xml",
"classpath:backoffice-servlet.xml"
})
public class UserControllerTests {
#Autowired
private MockMvc mockMvc;
#Before
void setup(WebApplicationContext wac) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
..
}
but when I start the test I got this error:
rg.junit.runners.model.InvalidTestClassError: Invalid test class 'com.pastis.UserControllerTests':
1. Method setup() should be public
2. Method setup should have no parameters
Here an example:
RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = MyWebConfig.class)
public class CustomerControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup () {
DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
this.mockMvc = builder.build();
}
#Test
public void testUserController () throws Exception {
ResultMatcher ok = MockMvcResultMatchers.status()
.isOk();
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/customers");
this.mockMvc.perform(builder)
.andExpect(ok);
}
}
So I explain the output of the exception:
Add the modifier public to your method setup, otherwise JUnit can't invoke it
Remove the parameter from the method, #Before, #After and such don't allow parameters.
How to setup MockMvc correctly is another question. Recent Spring and additional annotations regarding web scope initialization and -behavior leave the scope of this answer. (This also requires more clarification, for example which JDK, which Spring or Spring Boot… XML configuration, dbunit and JUnit 4 suggest a legacy context.)

Spring Boot + TestNG + MockMVC gives Null for #Autowired

I'm new to Spring-Boot, TestNG and MockMVC, when i try to write TestNG test case it gives Null for below:
#Autowired
private WebApplicationContext webApplicationContext;
and also it gives null for
#BeforeTest
public void start()
{
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
Also, based on previous post from stackoverflow by moving
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); to #Test also the problem not resolved.
Below my code snippet
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#BeforeTest
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void testEmployee() throws Exception {
mockMvc.perform(get("/employee")).andExpect(status().isOk())
.andExpect(content().contentType("application/json;charset=UTF-8"))
.andExpect(jsonPath("$.name").value("emp1")).andExpect(jsonPath("$.designation").value("manager"))
.andExpect(jsonPath("$.empId").value("1")).andExpect(jsonPath("$.salary").value(3000));
}
Any link or working sample shared will be great help.
thanks
If you want to autowire WebApplicationContext, your test class should be annotated with #ContextConfiguration and #WebAppConfiguration.
You can find an example-ish here (Spring Framework's official repository).

How do I wire up the correct Spring context to my Cucumber tests?

I have a working integration test for my Spring Web MVC app that looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ShibaApplication.class)
#WebAppConfiguration
public class EchoControllerTests {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
private void setup() throws Exception {
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void echo() throws Exception {
mockMvc.perform(get("/echo/blargh"))
.andExpect(status().isOk())
.andExpect(content().string("blargh"));
}
}
Leaving that (successful) test in place, I tried to create an identical Cucumber test. The Cucumber runner is:
#RunWith(Cucumber.class)
#CucumberOptions(features="src/test/resources",
glue={"co.masslab.shiba", "cucumber.api.spring"})
public class CucumberTests {
}
The class that defines the Cucumber steps looks like:
#WebAppConfiguration
#Import(ShibaApplication.class)
#ContextConfiguration(classes=CucumberTests.class)
public class WebStepDefs {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
private ResultActions resultActions;
#When("^the client calls the echo endpoint$")
public void the_client_calls() throws Exception {
Assert.notNull(webApplicationContext);
this.mockMvc = webAppContextSetup(webApplicationContext).build();
this.resultActions = mockMvc.perform(get("/echo/blargh"));
}
#Then("^the client receives a status code of 200$")
public void the_client_receives_a_status_code() throws Exception {
resultActions.andExpect(status().isOk());
}
}
However, the cucumber test fails, as the result is not a 200 but a 404.
I suspect this is because the WebApplicationContext getting autowired into the WebStepDefs class isn’t the same as the one that gets autowired into the EchoControllerTests. I’ve been going over the Spring JavaConfig Reference Guide v1.0.0.M4, but I haven’t yet figured out where I’m going wrong.
I kept trying different combinations of annotations, and finally figured this one out. The annotations for WebStepsDef that worked for me were:
#ContextConfiguration(classes=ShibaApplication.class, loader=SpringApplicationContextLoader.class)
#IntegrationTest
#WebAppConfiguration

Spring MVC Testing: Controller Method Parameters

I am trying to write tests for my Spring MVC web application.
I have successfully configured a MockMvc object and can execute preform() operations, and can verify that my controller methods are being called.
The issue I am experiencing has to do with passing in a UserDetails object to my controller methods.
My controller method signature is as follows:
#RequestMapping(method = RequestMethod.GET)
public ModelAndView ticketsLanding(
#AuthenticationPrincipal CustomUserDetails user) {
...
}
During the tests, user is null (which is causing a NullPointerException due to my code.
Here is my test method:
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
#Test
public void ticketsLanding() throws Exception {
// testUser is populated in the #Before method
this.mockMvc.perform(
get("/tickets").with(user(testUser))).andExpect(
model().attributeExists("tickets"));
}
So my question is how do I properly pass a UserDetails object into my MockMvc controllers? What about other, non-security related objects such as form dtos?
Thanks for your help.
You need to init the security context in your unit test like so:
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
I use the following setup:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {
"classpath:/spring/root-test-context.xml"})
public class UserAppTest implements InitializingBean{
#Autowired
WebApplicationContext wac;
#Autowired
private FilterChainProxy springSecurityFilterChain;
// other test methods...
#Override
public void afterPropertiesSet() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(wac)
.addFilters(springSecurityFilterChain)
.build();
}
}

Categories

Resources