Home > OS >  Spring Boot Unit Testing MockMvc Null Body
Spring Boot Unit Testing MockMvc Null Body

Time:11-20

I am having dificulties with using MockMvc. Here I have simple Service and controller classes:

Service:

@Slf4j
@Service
public class EmployeeService {
    //...

    public Employee GetSample() {
    //...
    //filling Employee Entities
    return new Employee(
            "Harriet"
            , "random"
            , 25);
    }
}

controller:

@RestController
@RequestMapping(value = "/info")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Validated
public class EmployeeController {
    private final EmployeeService employeeService;

    @PostMapping("/GetEmployee")
    public ResponseEntity<Employee> GetEmployee() {
       Employee employee = employeeService.GetSample();
       return new ResponseEntity<>(employee, HttpStatus.OK);
    }
}

Test:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class EmployeeTestCase {
    private MockMvc mockMvc;

    @InjectMocks
    private EmployeeController EmployeeController;

    @Mock
    private EmployeeService employeeService;

    @Before
    public void setUp() {
        this.mockMvc = MockMvcBuilders.standaloneSetup(employeeController).build();
    }

    @Test
    public void getEmployee() throws Exception {
        this.mockMvc.perform(MockMvcRequestBuilders.post("/info/GetEmployee")).andDo(print());
    }
}

when I try to use MockMvc I get null body. It seems employee is null. But I didn't understand why. I thought that when test uses perform, it should initialise employee and later on it should't be null. I tried to cut the code as much as possible. I hope it is not long.

Note : also tried to use Mockito.when(employeeController.GetEmployee()).thenCallRealMethod();

CodePudding user response:

The @SpringBootTest annotation loads the complete Spring application context. That means you do not mock your layers (Services/Controllers).

If you wanted to test specific layers of your application, you could look into test slice annotations offered by Springboot: https://docs.spring.io/spring-boot/docs/current/reference/html/test-auto-configuration.html

In contrast, a test slice annotation only loads beans required to test a particular layer. And because of this, we can avoid unnecessary mocking and side effects.

An example of a Test Slice is @WebMvcTest

@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = HelloController.class,
        excludeFilters = {
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SecurityConfig.class)
        }
)
public class HelloControllerTest {

    @Autowired
    private MockMvc mvc;


    @Test
    public void hello() throws Exception {
        String hello = "hello";

        mvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andExpect(content().string(hello));
    }


    @Test
    public void helloDto() throws Exception {
        String name = "hello";
        int amount = 1000;

        mvc.perform(
                        get("/hello/dto")
                                .param("name", name)
                                .param("amount", String.valueOf(amount)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name", is(name)))
                .andExpect(jsonPath("$.amount", is(amount)));
    }
}

However if you really wanted to load up the SpringBoot Application context, say for an Integration Test, then you have a few options:

@SpringBootTest
@AutoConfigureMockMvc
public class TestingWebApplicationTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void shouldReturnDefaultMessage() throws Exception {
        this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
                .andExpect(content().string(containsString("Hello, World")));
    }
}

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class AuctionControllerIntTest {

    @Autowired
    AuctionController controller;

    @Autowired
    ObjectMapper mapper;

    MockMvc mockMvc;

    @Before
    public void setUp() throws Exception {
        System.out.println("setup()...");
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }

    @Test
    public void create_ValidAuction_ShouldAddNewAuction() throws Exception {
        final Auction auction = new Auction(
                "Standing Desk",
                "Stand up desk to help you stretch your legs during the day.",
                "Johnnie34",
                350.00);

        mockMvc.perform(post("/auctions")
                .contentType(MediaType.APPLICATION_JSON)
                .content(toJson(auction)))
                .andExpect(status().isCreated());
    }
}

Lets say you don't want to load up any layers at all, and you want to mock everything, such as for example a Unit Test:

@RunWith(MockitoJUnitRunner.class)
class DemoApplicationTest {

    @Mock
    private UserRepository userRepository;

    private Demo noneAutoWiredDemoInstance;

    @Test
    public void testConstructorCreation() {

        MockitoAnnotations.initMocks(this);
        Mockito.when(userRepository.count()).thenReturn(0L);

        noneAutoWiredDemoInstance = new Demo(userRepository);

        Assertions.assertEquals("Count: 0", noneAutoWiredDemoInstance.toString());
    }
}
  • Related