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.)
Related
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.
Spring Boot here. I currently have the following REST controller:
#RestController
public class FizzbuzzController {
private final FizzbuzzService FizzbuzzService;
public FizzbuzzController(FizzbuzzService FizzbuzzService) {
this.FizzbuzzService = FizzbuzzService;
}
#PostMapping("/Fizzbuzzs/{fizzbuzzId}")
public ResponseEntity<FizzbuzzDTO> addFizzbuzz(#RequestParam("files") List<MultipartFile> files,
#PathVariable String fizzbuzzId) throws IOException {
FizzbuzzDTO fizzbuzzDTO = fizzbuzzService.store(files, fizzbuzzId);
return ResponseEntity.status(HttpStatus.OK).body(fizzbuzzDTO);
}
}
I would like to write an integration test for it that:
Mocks or stubs an HTTP request to the URL; and
Allows me to inject the FizzbuzzController (under test) with a mocked FizzbuzzService or the real thing; and
Allows me to inspect the HTTP response coming back from the method (check status code, check response entity, etc.)
My best attempt thus far:
#WebMvcTest(FizzbuzzController.class)
#EnableConfigurationProperties
#AutoConfigureMockMvc
public class FizzbuzzControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private FizzbuzzService FizzbuzzService;
#Test
public void should_store_fizzbuzz_files() throws Exception {
// I can't even get the test to run
assertTrue(1 == 1);
}
}
When I run this, the test fails to run and it is clear (looking at the logs) that Spring is loading the entire application context of my app, whereas I just want it to isolate the context to this test class, the main FizzbuzzController class, and anything in the dependency tree underneath it.
Can anyone spot where I'm going awry?
You need another context for testing. I'm suggesting you to have a separate Test config:
#TestConfiguration
#Slf4j
#EnableJpaRepositories("tth.patientportal.repository")
public class TestConfig { // bean configs goes here for testing if you need to change
// context}
and in a controller test build the context like below:
#RunWith(SpringRunner.class)
#AutoConfigureTestEntityManager
#SpringBootTest
#TestPropertySource("classpath:application-unittest.properties")
#ContextConfiguration(classes = {TestConfig.class})
public class RestControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Before
public void setup()
{
mockMvc = MockMvcBuilders.
webAppContextSetup(webApplicationContext)
.build();
}
#Test
public void shouldReturnRegisteredUser() throws Exception {
this.mockMvc.
perform(MockMvcRequestBuilders
.post("url")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.username").exists());
}
}
I have two integration test in my program and unfortunately both doesn't work. I don't know this is good idea to write both problem in one case but i try.
Firstly i show my db integration test :
#RunWith(SpringRunner.class)
#DataJpaTest
public class TeamDatabaseIntegrationTest {
#MockBean
private TeamRepository teamRepository;
#Autowired
private TestEntityManager testEntityManager;
#Test
public void testDb() {
Team team = new Team(1L, "teamName", "teamDescription", "krakow", 7);
Team team2 = new Team(2L, "teamName", "teamDescription", "krakow", 7);
testEntityManager.persist(team);
testEntityManager.flush();
Iterable<Team> teams = teamRepository.findAll();
assertThat(teams).hasSize(2).contains(team, team2);
}
In this test i add 2 elements to my db and expect that this test is ok but it return this :
java.lang.AssertionError:
Expected size:<2> but was:<0> in:
<[]>
In my second test i want to test controller method show all elements.
This is my method in cotroller:
#GetMapping("/teams")
public List<TeamDto> findAll() {
return teamService.findAll();
}
My test method for this look like this :
#SpringJUnitWebConfig(classes = CrewApplication.class)
public class TeamControllerMethodIntegrationTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setup() throws Exception
{
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
MockitoAnnotations.initMocks(this);
}
#Test
void getAccount() throws Exception {
this.mockMvc.perform(get("/teams")
.accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json;charset=UTF-8"))
.andExpect(jsonPath("$version").value(null))
.andExpect(jsonPath("$name").value("Apacze"))
.andExpect(jsonPath("$createOn").value(null))
.andExpect(jsonPath("modifiedOn").value(null))
.andExpect(jsonPath("$description").value("grupa programistow"))
.andExpect(jsonPath("$city").value("Włocławek"))
.andExpect(jsonPath("$headcount").value(null));
}
}
In this case i have other error.
java.lang.NullPointerException
at com.softwaremind.crew.people.integrationTest.TeamControllerMethodIntegrationTest.getAccount
I fight with this tests over week and i really have no idea how to repair this.
In your first case replace
#MockBean
private TeamRepository teamRepository;
with
#Autowired
private TeamRepository teamRepository;
(you cannot use mock and expect it to return values from in memory db)
For you second test. Remove #SpringJUnitWebConfig and annotate it with
#RunWith(SpringRunner.class)
#WebMvcTest()
and add Autowired to MockMvc
#Autowired
private MockMvc mockMvc;
EDIT
Additionally remove your setup() method as everything should be already configured. (and then you also don't need webApplicationContext attribute) and assert that the Test annotation you are using is org.junit.Test (check your imports)
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).
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