3

I have :

  • an interface : EntityService
  • a first implementation : EntityServiceImpl - This class is annotated with @Primary
  • an other one : EntityServiceClientImpl
  • and a controller that has this field @Autowired EntityService

I would like to do a test on this controller and for this test to be unitary I mock EntityService.

So of course this code does not work because Spring detects two beans annotated with Primary :

@Configuration
class EntityControllerTestConfig {
    @Bean
    @Primary
    EntityService entityService() {
        return mock(EntityService.class);
    }
}

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
@WebAppConfiguration
@ContextConfiguration(classes = EntityControllerTestConfig.class)
public class EntityControllerTest {

    @Autowired
    private EntityService entityService;

...

@SpringBootApplication(scanBasePackages= "com.company.app")
@EntityScan (basePackages = {"com.company.app" }, basePackageClasses = {Jsr310JpaConverters.class })
@EnableJpaRepositories("com.company.app")
public class TestApplication {
    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
}

I tried to find an other way to mock and to exclude EntityServiceClient on test configuration but i was not able to mock. (cf : exclude @Component from @ComponentScan )

DamDam
  • 133
  • 1
  • 9

2 Answers2

1

I finaly found that solution : a spring context (with controller, controllerAdvice and mock of service) and not a spring boot context

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class EntityControllerTest {

    @Configuration
    public static class EntityControllerTestConfig {
        @Bean
        public EntityService entityService() {
            return mock(EntityService.class);
        }

        @Bean
        public EntityController entityController() {
            return new EntityController(entityService());
        }
    }

    @Autowired
    private EntityService entityService;

    @Autowired
    private EntityController entityController;

    private MockMvc mockMvc;

    @Before
    public void setup() throws DomaineException {
        this.mockMvc = MockMvcBuilders
            .standaloneSetup(entityController)
            .setControllerAdvice(new myControllerAdvice())
            .build();

NB : Since Spring 4.2 you can set your ControllerAdvice like that.

DamDam
  • 133
  • 1
  • 9
0

You can approach it slightly differently and combine @WebMvcTest with @MockBean annotation to test just the controller with it's own minimal context.

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = EntityController.class)
public class EntityControllerTest {

  @MockBean
  private EntityService entityService;

  @Autowired
  private MockMvc mvc;

In this example EntityService will be mocked while MockMvc can be used to assert the request mappings in controller.

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
  • Hi Karol, That did not worked (`Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test`). Is it possible to mock a bean after the initialisation ? – DamDam Apr 06 '18 at 09:36
  • @DamDam you are using `@SpringBootApplication` and this one provides `@SpringBootConfiguration`. You must have set up the project in a very unusual way that it doesn't work out of the box. Check https://start.spring.io/ to get a fresh clean project setup. – Karol Dowbecki Apr 06 '18 at 09:39