Working on my Spring 2.67 and Spock 2.1-groovy-3.0 testing. I have the basic testing working but now trying some integration testing without success. I have a controller with:
private ApiService apiService;
@Autowired
public ApiController(ApiService apiService) {
this.apiService = apiService;
}
@GetMapping("api/{scannedId}")
@ResponseBody
public ResponseEntity getScannedId(@PathVariable String scannedId) {
try {
logger.info("ApiKey Controller received GET /api/" + scannedId);
ApiKey found = apiService.retrieveValidApiKey(scannedId);
...
}
...
The apiService has :
private ApiRepository apiRepository;
@Autowired
public ApiService(ApiRepository apiRepository) {
this.apiRepository = apiRepository;
}
public ApiKey retrieveValidApiKey(String uuid) {
ApiKey anApi = apiRepository.getApiKeyByApiKey(uuid);
if (anApi == null) {
logger.info("ApiService.retrieveValidApiKey({}) failed to find a matching ApiKey", uuid);
return null;
}
I have a Spock test that seeds the database with two values and then successfully calls the /api endpoint. I have code in the test that confirms the two values were inserted, but when the actual ApiService class is called, they are not found:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureTestDatabase
class ApiControllerTest extends Specification {
@Shared Logger logger = LoggerFactory.getLogger(this.getClass())
@Autowired
ApiController apiController
@Autowired
ApiRepository apiRepository
@Transactional
def "GET by scannedId using apikey #apiKey should be #resultCode"() {
given:
def foundList = apiRepository.findAll()
logger.info("{} apiKeys in repository", foundList.size())
for (int i = 0; i < foundList.size(); i++) {
logger.info("Found ApiKey #{} apiKey: {} & uuid: {}", i, foundList.get(i).apiKey, foundList.get(i).uuid)
}
when:
def foundListCount = apiRepository.getApiKeyByApiKey(apiKey)
logger.info("FoundList: {}", foundListCount)
ResponseEntity<ApiKey> result = restTemplate.getForEntity( "/api/{scannedId}", ApiKey.class, apiKeyValue1)
logger.info("TestRestTemplate returned apikey: {}", result)
then:
assert result.getStatusCode() == resultCode
where:
apiKey || resultCode
"testApiKey3" || HttpStatus.NOT_FOUND
apiKeyValue1 || HttpStatus.OK
apiKeyValue2 || HttpStatus.OK
}
def setup() {
def apiKey1 = new ApiKey(apiKey: apiKeyValue1, uuid: uuid1, beginDate: beginDate1, endDate: endDate1)
def apiKey2 = new ApiKey(apiKey: apiKeyValue2, uuid: uuid2, beginDate: beginDate2, endDate: endDate2)
apiRepository.saveAndFlush(apiKey1)
apiRepository.saveAndFlush(apiKey2)
}
When I run the test, the logger in the test method spits out all the persisted values. But the test fails because the ApiService.getScannedId fails because it does not see the values persisted in test setup.
I cannot use the @DataJpaTest because the ApplicationContext isn't loaded then, so the endpoints fail.
I am not sure why Spock sees the values persisted via Repository, but the ApiService doesn't. Is it a context issue? I really would like to test without mocks here if at all possible.
The problem is that your test is annotated with @Transactional
that means that only things that run in that method can see the data. The rest request you are sending out, will be handled by another thread that doesn't have access to the transaction and thus will not see the data.
You'll have to remove the annotation if you want it to work, but then you'll also have to clean the inserted data manually at the end of the test/cleanup()
, since you can't rely on the transaction rollback.