WebApplicationContext is always null when running Geb integration test - spring

Why isn't my context being injected? The application runs fine. Integration tests using Spring's junit runner and MockMvc run fine. Do I need to add something to integrate Spring and Spock?
#ContextConfiguration(classes = MyConfiguration)
#WebAppConfiguration
class BetaRequestTest extends GebReportingSpec{
#Autowired
WebApplicationContext webApplicationContext; //always null
def setup() {
browser.driver = MockMvcHtmlUnitDriverBuilder
.webAppContextSetup(webApplicationContext).build()
}
def destroy(){
browser.driver?.close()
}
#Test
def void "should render the page"(){
setup:
to BetaRequestPage
expect:
$('h1').value() == "Welcome to Foo"
}
}

Figured it out.
I needed to add the following test dependency:
testCompile "org.spockframework:spock-spring:1.0-groovy-2.4"

Related

autowore mockmvc is null in controller test after I upgrade spring framework to 6.0.1

using:
springboot version is 3.0.1
spring framework version 6.0.1
java version 17
after I upgrade my spring framework version, I refactor security configuration to this
#WebMvcTest(NominationController.class)
#AutoConfigureMockMvc
class NominationIsolationTest extends Specification {
#Autowired
MockMvc mockMvc
...
def "should return status created with response when post nominations given valid nomination"() {
given:
...
when:
def response = mockMvc")
.content(body).contentType(MediaType.APPLICATION_JSON))
then:
...
}
but it failed, and I debug find that the mockMvc' value is null, and I use spock to do the controller test
I have try to add #SpringBootTest annotation, but not work
before I upgrade, the controller test can run successfully
now I fixed this issue, by add #ContextConfiguration annotation on test class, and autowire WebApplicationContext then use mvcbuilder to create mockmvc, after that, it fixed
#WebMvcTest
#ContextConfiguration
class IsolationTest extends Specification {
#Autowired
WebApplicationContext wac
MockMvc mockMvc
def setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build()
}

Spring boot test doesn't Autowire all dependencies

I have a bit of a confusing error in my test scenario.
We want to refactor an Application that is not tested at all. To ensure that we have the same outcame after refactoring I'll write some integration tests for one Controller class.
#RestController
#RequestMapping("/rfq")
public class RfqController {
#Autowired
private RfqRepository rfqRepo;
#Autowired
private RfqDao rfqDao;
...
#PostMapping("/get")
public #ResponseBody BuyerRfqView getRFQ(#RequestBody SingleIdBody body) {
int id = body.getId();
Optional<Rfq> rfq = rfqRepo.getById(id);
...
}
}
In that case I want to test with testcontainers and spring-boot-test everything worked well, containers are up and running and the application starts so far. But the problem is that at runtime the spring-boot-test doesn't Autowire rfqRepo in the class under test. In the Testclass, every single dependency is in the ComponentScan or EntityScan and the repositories are also injected. I have no clue why this is not working. when the test is running I get a Nullpointer Exception by rfqRepo ...
here is the Test class:
#SpringBootTest(classes = RfqController.class, webEnvironment =
SpringBootTest.WebEnvironment.RANDOM_PORT)
#ComponentScan({...})
#EnableJpaRepositories({...})
#EntityScan({...})
#EnableAutoConfiguration
#ActiveProfiles("local")
#Testcontainers
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class RfqControllerTest {
#Container
private static OracleContainer database = new OracleContainer(
"oracleinanutshell/oracle-xe-11g:latest")
.withExposedPorts(1521, 5500)
.withPassword("...");
#InjectMocks
RfqController rfqController;
#DynamicPropertySource
static void databaseProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", database::getJdbcUrl);
registry.add("spring.datasource.username", database::getUsername);
registry.add("spring.datasource.password", database::getPassword);
}
#BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
ScriptUtils.runInitScript(new JdbcDatabaseDelegate(database, ""), "ddl.sql");
}
#Test
void testGetRFQ() {
BuyerRfqView result = rfqController.getRFQ(new SingleIdBody(176501));
Assertions.assertEquals(new BuyerRfqView(), result);
}
}
In the SpringBootTest annotation you are only using RfqController. That's the only class then that is available during test.
#SpringBootTest(classes = RfqController.class, webEnvironment =SpringBootTest.WebEnvironment.RANDOM_PORT)
So you have to add all classes that are needed for your tests.

Why does #SpringBootTest run the #SpringBootApplication and skip the #Test method?

I created an integration test for a SpringBoot command line application following step 7 in this example:
Here's my integration test
#RunWith(SpringRunner.class)
#SpringBootTest
#TestPropertySource(locations = "classpath:application.integrationtest.properties")
public class IntegrationTest {
#Autowired
private RequestProcessor requestProcessor;
#Test
public void testRequestProcessor() {
requestProcessor.setStandloneFlag(true);
requestProcessor.processRequest();
}
}
When I run this test it launches the #SpringBootApplication itself, but never runs the #Test method in the integration test. The docs say that this should not happen.
Why does #SpringBootTest ignore my #Test method and launch the application?

Mock Spring service in controller test using Spock

I am looking for a way to mock a Service bean used in Controller so I can test only controller using MockMvc. But I can't find an easy way to replace real bean with Spock mock. Everything uses spring-boot 1.3.2 version. More details below:
I have a following controller class
#RestController
#RequestMapping(path = "/issues")
#AllArgsConstructor(onConstructor = #__(#Autowired))
public class NewsletterIssueController {
private final GetLatestNewsletterIssueService latestNewsletterIssueService;
#RequestMapping(
method = RequestMethod.GET,
path = "/latest"
)
public ResponseEntity getLatestIssue() {
Optional<NewsletterIssueDto> latestIssue = latestNewsletterIssueService.getLatestIssue();
if (latestIssue.isPresent()) {
return ResponseEntity.ok(latestIssue.get());
} else {
return ResponseEntity.notFound().build();
}
}
}
And Integration Spock test for this class:
#ContextConfiguration(classes = [Application], loader = SpringApplicationContextLoader)
#WebAppConfiguration
#ActiveProfiles("test")
class NewsletterIssueControllerIntegrationSpec extends Specification {
MockMvc mockMvc
#Autowired
GetLatestNewsletterIssueService getLatestNewsletterIssueService
#Autowired
WebApplicationContext webApplicationContext
def setup() {
ConfigurableMockMvcBuilder mockMvcBuilder = MockMvcBuilders.webAppContextSetup(webApplicationContext)
mockMvc = mockMvcBuilder.build()
}
def "Should get 404 when latest issue does not exist"() {
given:
getLatestNewsletterIssueService.getLatestIssue() >> Optional.empty() // this won't work because it is real bean, not a Mock
expect:
mockMvc.perform(MockMvcRequestBuilders
.get("/issues/latest")
.contentType(JVM_BLOGGERS_V1)
.accept(JVM_BLOGGERS_V1)
).andExpect(MockMvcResultMatchers.status().isNotFound())
}
}
I need a way to replace this autowired bean with a Mock/Stub so I can define interactions in 'given' section.
I'd create a local configuration in the test and override the bean there.
I don't know Groovy, but it would like this in Java:
#ContextConfiguration(classes = NewsletterIssueControllerIntegrationSpec.Conf.class, loader = SpringApplicationContextLoader.class)
#WebAppConfiguration
#ActiveProfiles("test")
class NewsletterIssueControllerIntegrationSpec extends Specification {
#Configuration
#Import(Application.class)
public static class Conf {
#Bean
public GetLatestNewsletterIssueService getLatestNewsletterIssueService() {
return mock(GetLatestNewsletterIssueService.class);
}
}
// […]
}
Caveat: This approach works well with Mockito, but you might need a pre-release version of Spock for it to work, ref: https://github.com/spockframework/spock/pull/546
By the way: Spring Boot 1.4 will provide a #MockBean construction to simplify this.
With Spock 1.2 you can use SpringBean annotation to inject mocked service in spring context https://objectpartners.com/2018/06/14/spock-1-2-annotations-for-spring-integration-testing/
So with this new annotation your test would be :
#WebMvcTest(controllers = [NewsletterIssueController], secure = false)
#AutoConfigureMockMvc
#ActiveProfiles("test")
class NewsletterIssueControllerIntegrationSpec extends Specification {
#Autowired
MockMvc mockMvc
#SpringBean
GetLatestNewsletterIssueService getLatestNewsletterIssueService
def setup() {
// nothing to do as SpringBean and WebMvcTest do the job for you
}
def "Should get 404 when latest issue does not exist"() {
given:
getLatestNewsletterIssueService.getLatestIssue() >> Optional.empty() // this won't work because it is real bean, not a Mock
expect:
mockMvc.perform(MockMvcRequestBuilders
.get("/issues/latest")
.contentType(JVM_BLOGGERS_V1)
.accept(JVM_BLOGGERS_V1)
).andExpect(MockMvcResultMatchers.status().isNotFound())
}
}

How to run tests using a custom WebApplicationInitializer?

class MyWebAppInitializer extends WebApplicationInitializer {
def onStartup(servletContext: ServletContext): Unit = {
...
}
}
#RunWith(classOf[SpringJUnit4ClassRunner])
#WebAppConfiguration
#ContextConfiguration(classes = Array(classOf[MyConfig]),
initializers=Array(classOf[MyWebAppInitializer])) // <<< ERROR
class MyTest {
...
}
Complains about :
annotation argument needs to be a constant; found: classOf[MyWebAppInitializer]
UPDATE: #M. Deinum points out that only ApplicationContextInitializers are allowed here - so the error is a badly reported type mistmatch.
So... how can I use my own MyWebAppInitializer in order and test the functionality defined therein?
In your context configuration I don't see that you have a context loader listed. The AnnotationConfigWebContextLoader will locate instances of WebApplicationInitializer on your classpath, by adding this and removing the intializers (which, as you have noted, are for ApplicationContextInitializers and not WebApplicationInitializers) then you should be all set.
#RunWith(classOf[SpringJUnit4ClassRunner])
#WebAppConfiguration
#ContextConfiguration(classes = {ConfigClass.class, AnotherConfigClass.class}, loader=AnnotationConfigWebContextLoader.class))
class MyTest {
...
Here is a working example
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes={WebConfiguration.class, SecurityConfig.class}, loader=AnnotationConfigWebContextLoader.class)
#ActiveProfiles("dev")
public class AppTests {
private MockMvc mockMvc;
#Autowired
protected WebApplicationContext webApplicationContext;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void simple() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("index"));
}
}
The answer is "Sorry,you can't".
You could refer to this:
Spring FrameworkSPR-10199
Add capability to use WebApplicationInitializer for testing Spring web applications
Just as Sam Brannen said:
Although Spring does provide mocks for the Servlet API, Spring does
not mock a Servlet container and currently has no intention to. Spring
has always focused on out of container integration testing. Fully
mocking a container is therefore beyond the scope of Spring's testing
support. Please see comments from Rossen and me above for further
details.

Resources