0

I've upgraded from Spring Boot 1.5 to Spring Boot 2.1.8. I had some tests that were working but are now failing. I also was using maven-surefire plugin at version 2.9 and it worked, but I upgraded that to 2.22.0 as well, if that matters.

@ExtendWith(SpringExtension.class)
@WebMvcTest(value = ElementController.class, secure = false)
@ContextConfiguration(classes = TestSite1Config.class)
public class ElementControllerSite1IT {
  @Autowired
  protected MockMvc mvc;
  @MockBean
  ElementService elementService;
  @BeforeEach
  public void setup() {
    when(elementService.getElementTable( ... )) //skipping args for brevity
       .thenReturn(new ElementTable());
  }

  @Configuration
  public static class TestSite1Config {
    @Bean
    @Autowired
    public ElementController elementController(final ElementService elementService) {
      return new ElementController(elementService, new ElementControllerProperties(DeploymentLocation.SITE1));
  }

  @Test
  public void failSite1ValidationWithoutId() throws Exception {
    ElementParameters params = getParams(false);
    mvc.perform(post("/element")
      .contentType(JSON)
      .andExpect(status().isBadRequest());
  }

  //more tests, but doesn't matter.
}

There's another class like above, but replace Site1 with Site2.

There is an ElementController & Service class as well.

I get this exception:

Caused by BeanDefinitionOverrideException: Invalid bean definition with name 'elementController' defined in class path resource [ui/v2/web/ElementControllerSite1IT$TestSite1Config.class]: Cannot register bean definition [Root bean: class [null]; ... defined in class path resource [ui/v2/web/ElementControllerSite1ITConfig.class] for bean 'elementController': There is already [Generic bean: class [ui.v2.web.ElementController]; .. defined in file [...ui/v2/web/ElementController.class]] bound.

I didn't write the tests, it's code that I've inherited, in a code base that I'm just getting spooled up on.

  • It should have failed running in the first place. Remove the `TestSite1Config, or at least remove the controller and only return the `ElementControllerProperties`. Spring will take care of injecting the right properties in the `ElementController` that is what `ElementController` in the `@WebMvcTest` is for. – M. Deinum Sep 25 '19 at 18:35
  • Why would it have failed? Before, it would automatically override the elementController bean (for the real class) with the mocked up one created here. Can you explain, or throw me a link to something that might shed light on this? I tried removing the TestSite1Config and it broke worse. What would go in the ContextConfiguration annotation? – Christopher Gross Sep 25 '19 at 19:03
  • As I mentioned in my initial comment you should have a config with the `ElementControllerProperties` only. The Spring test part will take care of creating the `ElementController` as that is what the `@WebMvcTest` is responsible for. – M. Deinum Sep 26 '19 at 05:23
  • I'm still a bit confused. There's a few things that are "config"s. Are you saying that my TestSite1Config should just return the ElementControllerProperties? So: ``` @Configuration public static class TestSite1Config { @Bean public ElementControllerProperties elementControllerProperties() { return new ElementControllerProperties(DeploymentLocation.SITE1); } ``` Doing this, in my tests I get a 404 for whatever I throw at it, which at least means it is wiring something up? Why is this not formatting like code?? Sorry =( – Christopher Gross Sep 26 '19 at 13:10
  • That is what it should return. That together with the mocked service should be enough to create the `ElementController`. Although the 404 is weird, could you share the logging? – M. Deinum Sep 26 '19 at 13:13
  • Sharing the logging is tricky. It's on a test network so I'd need to transcribe it over. And I dont have logging on the tests, the error that I get is that we expect other statuses (200 for the one that should work) and I get the 404, casuing the assertions to fail. – Christopher Gross Sep 26 '19 at 13:17
  • You can run the test locally and inspect the logging. It should explain what is registered etc. – M. Deinum Sep 26 '19 at 13:18
  • I didn't scroll through enough of the errors -- now I'm getting the duplicate bean error for my `ElementControllerProperties` (which is why I'm getting the 404 -- it's not setting up the controller). – Christopher Gross Sep 26 '19 at 13:27
  • If I remove the `@RestController` annotation on my ElementController class, then it will get through the tests and wire up correctly. So what am I missing? – Christopher Gross Sep 26 '19 at 15:51
  • So your `ElementControllerProperties` is also already a bean. Are properties being mapped to it from a properties file? If so you can use `@TestPropertySource` to specify the value to have it configured just for your test. – M. Deinum Sep 26 '19 at 17:28
  • No, its all autowired, otherwise I would just hack the files. Is there a way I could use the Qualifier annotation to just force it to use the one I want it to? – Christopher Gross Sep 26 '19 at 17:50
  • Then how is the `ElementControllerProperties` normally being wired? The enum value must come from somewhere? – M. Deinum Sep 30 '19 at 05:43

3 Answers3

1

You could try @TestPropertySource(properties ="..." :

@ExtendWith(SpringExtension.class)
@WebMvcTest(value = ElementController.class, secure = false)
@ContextConfiguration(classes = TestSite1Config.class)
@TestPropertySource(properties = {"spring.main.allow-bean-definition-overriding=true", "local.server.port=7777"})
public class ElementControllerSite1IT {
 ...
}
i.bondarenko
  • 3,442
  • 3
  • 11
  • 21
  • I tried this, it doesn't work. I get the same error as above. – Christopher Gross Sep 25 '19 at 18:03
  • I have just reproduced this error locally and my configuration works properly. Did you try it as is, with in place values (without test-application.properties)? – i.bondarenko Sep 25 '19 at 18:13
  • Yeah I tried with both the hard coded value like you gave, in addition to inside my props file. No dice. Maybe there's something else in the pom that I need to change versions on? Could you post your pom.xml for building? – Christopher Gross Sep 25 '19 at 19:08
  • I have commit working example into github. You can clone it and chek: https://github.com/ivan333m/mock.git. Have a look at https://github.com/ivan333m/mock/blob/master/demo/src/test/java/com/example/demo/rest/EmployeeControllerTest.java. RestTemplate is overridden – i.bondarenko Sep 26 '19 at 04:29
0

Add spring.main.allow-bean-definition-overriding=true to application.properties

Dmitry
  • 864
  • 7
  • 15
  • I tried adding that using @TestPropertySource (in my test-application.properties) file and as a direct injection, and it didnt work. I'd like also to understand *why* it doesn't work as coded, and not just undo the change that Spring made for how the bean overriding behaves. – Christopher Gross Sep 25 '19 at 17:01
0

Got it working with this: (for anyone who stumbles upon this question)

@ExtendWith(SpringExtension.class)
@AutoConfigureMockMvc
@WebMvcTest
@ContextConfiguration(classes = {ElementController.class,TestSite1Config.class})
public class ElementControllerSite1IT {
  @Autowired
  private MockMvc mvc;
  ...
  @Configruation
  public static class TestSite1Config {
    @Bean
    @Primary
    public ElementControllerProperties elementControllerProperties() { return ... }
  }
  ...
}
  • It went haywire (I think someone else added some code that messed things up). I ended up using profiles since that was way cleaner and worked (and insulated the other tests from these changes). See this item: https://stackoverflow.com/questions/39041542/override-a-single-configuration-class-on-every-spring-boot-test – Christopher Gross Oct 02 '19 at 13:09