3

I have a problem with mocking service in Spring MVC:

@Controller
public class CompanyController {

  @Autowired
  private CompanyService companyService;

  @Autowired
  private CompanyRelationService companyRelationService;

  @GetMapping({"/", "/companies"})
  public String displayCompanies(Model model) {
    model.addAttribute("company", new Company());
    List<Company> companies = companyService.findAll();
    model.addAttribute("companies", companies);
    return "companies";
  }
}

and test:

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

@Autowired
private WebApplicationContext webApplicationContext;

@Mock
CompanyService companyServiceMock;

private MockMvc mockMvc;


@Before
public void setUp() {
    Mockito.reset(companyServiceMock);
    mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    MockitoAnnotations.initMocks(this);
}


@Test
public void shouldListAllCompanies() throws Exception {
    Company company1 = new Company("company1", new Address());
    Company company2 = new Company("company2", new Address());

    when(companyServiceMock.findAll()).thenReturn(Arrays.asList(company1, company2));

    mockMvc.perform(get("/companies"))
            .andExpect(status().isOk())
            .andExpect(view().name("companies"))
            .andExpect(model().attribute("companies", hasSize(2)))
            .andExpect(model().attribute("companies", hasItem(
                    allOf(
                            hasProperty("name", is("company1")))
            )))
            .andExpect(model().attribute("companies", hasItem(
                    allOf(
                            hasProperty("name", is("company2"))
                    )
            )));

}
}

The question is why I get companies from real service instead of mock (company1, company2):

java.lang.AssertionError: Model attribute 'companies'
     Expected: a collection containing (hasProperty("name", is "company1"))
     but: hasProperty("name", is "company1") property 'name' was "companyFromRealService", 
     hasProperty("name", is "company1") property 'name' was "CompanyFromRealService2"

Updated Test class, removed setUp and changed @Bean into @MockBean, but remain @SpringBootTest and it works:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class CompanyTests {

@MockBean
private CompanyService companyServiceMock;

@Autowired
private MockMvc mockMvc;

@Test
@WithMockUser(roles = "ADMIN")
public void shouldListAllCompanies() throws Exception {
    Company company1 = new Company("company1", new Address());
    Company company2 = new Company("company2", new Address());

    when(companyServiceMock.findAll()).thenReturn(Arrays.asList(company1, company2));

    mockMvc.perform(get("/companies"))
            .andExpect(status().isOk())
            .andExpect(view().name("companies"))
            .andExpect(model().attribute("companies", hasSize(2)))
            .andExpect(model().attribute("companies", hasItem(
                    allOf(
                            hasProperty("name", is("companyFromRealService1")))
            )))
            .andExpect(model().attribute("companies", hasItem(
                    allOf(
                            hasProperty("name", is("companyFromRealService2"))
                    )
            )));
}

}

10
  • What happens on the get("/companies")? I guess it calls real service. Replace the call with mock results Commented Aug 21, 2017 at 11:27
  • Ofcourse they won't... You are working against the framework. Spring Boot does a lot of work injecting and mocking the dependencies and the first thing you do is destroy all that work. Remove your @Before method and put @Autowired on the MockMvc field and restart your test. Commented Aug 21, 2017 at 11:30
  • @StanislavL how can i do that? Commented Aug 21, 2017 at 11:34
  • 1
    Which Spring Boot version are you using? Add @AutoConfigureMockMvc to your test class. Commented Aug 21, 2017 at 11:37
  • 2
    Replace @Mock with @MockBean . Commented Aug 21, 2017 at 13:28

1 Answer 1

3

First of all, if you are just testing controller slice of your application, you should use @WebMvcTest annotation instead of @SpringBootTest (you can find more information here). You can use it like this : @WebMvcTest(CompanyController.class).

Secondly why are you getting into trouble with MockMvc in setUp() method? You can erase that setUp method as people suggest in comments and @Autowire MockMvc.

Finally, as you are using spring boot, it is better to use @MockBean instead of @Mock which is a wrapped version of it inside the spring library.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for answer. I did it and now i have java.lang.IllegalStateException: Failed to load ApplicationContext error.
@Helosze, Can you update your answer with the current test class?
When I changed Mock to MockBean, but didn't change SpringBootTest into WebMvcTest, now it works
@Helosze, happy to help. You may still want to update your test class final form to your question, so people can see it easly in the future if they end up in this question. And you can accept this answer if it solved your problem :)
Yes, of course. I've edited it. But maybe it will be good to change this SpringBootTest into WebMvcTest? :) Thanks for your help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.