Spring-Boot and Spock fail when using Unroll - spring

I'm setting up a demo project with Spring-Boot. For entity-persistence, I'm using Spring generated Repository implementations based on interfaces:
#Repository
public interface MovieRepository extends JpaRepository<Movie, Long> {
List<Movie> findByNameContaining(String name);
List<Movie> findByRelease(LocalDate release);
List<Movie> findByReleaseBetween(LocalDate start, LocalDate end);
List<Movie> findByNameContainingAndRelease(String name, LocalDate release);
}
To test this, I'm using Spock with Groovy, which works wonders:
#RunWith(SpringRunner.class)
#ContextConfiguration
#SpringBootTest
class MovieRepositoryTest extends Specification {
#Autowired
MovieRepository movieRepository
#Test
def findByNameContaining_shouldFindCorrectMovies() {
given:
movieRepository = this.movieRepository
when:
def result = movieRepository.findByNameContaining("Iron Man")
then:
result.size() == 3
}
}
But as soon as I try to mix in Spock's #Unroll, everything falls apart:
#Test
#Unroll
def findByNameContaining_shouldFindCorrectMovies() {
given:
movieRepository = this.movieRepository
when:
def result = movieRepository.findByNameContaining(query)
then:
result.size() == expected
where:
query || expected
"Iron Man" || 3
"Hulk" || 1
"Thor" || 3
"Avengers" || 3
"Thanos" || 0
"" || 20
}
Results in:
[INFO] Running com.spring.boot.demo.repositories.MovieRepositoryTest
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.003 s <<< FAILURE! - in com.spring.boot.demo.repositories.MovieRepositoryTest
[ERROR] initializationError(com.spring.boot.demo.repositories.MovieRepositoryTest) Time elapsed: 0.003 s <<< ERROR!
java.lang.Exception: Method $spock_feature_0_0 should have no parameters
I'm out of clues to what may cause this.
Any help is welcome.
Thank you
Edit 1:
Well, this is interesting. I've tried the following:
* Remove #Test -> java.lang.Exception: No runnable methods
* Remove #RunWith and #ContextConfiguration -> Unroll works, but movieRepository is not injected / wired: java.lang.NullPointerException: Cannot invoke method findByNameContaining() on null object
Fiddling with the different annotations hasn't resulted in a working scenario though. Any guesses?

Yesss, I've got it:
The RunWith was the culprit. In my Edit1, I noticed the difference with removing #Test. That got me thinking that I may be confusing JUnit testing with Spock testing. Also, the No runnable methods got me thinking. And leaving out the #RunWith since it is mostly absent in other Spock & Spring examples seemed like a good idea. And having Spring beans wired up with #ContextConfiguration is quite nice ;-). Apparently, #SpringBootTest doesn't do this?

Related

Spring Boot / Junit5+Mockito: Mock's mockitoInterceptor gets replaced during test?

I have two service classes (there are more, of course, but those two are relevant here), which are in use during an integration test.
For test, I set up a mock (ConfigurationService) and stub two methods:
#ExtendWith(SpringExtension.class)
#ActiveProfiles("test")
#SpringBootTest
#TestPropertySource(properties =
"spring.main.allow-bean-definition-overriding=true")
public class VehicleAuditExecutionServiceIT {
#MockBean
private ConfigurationService configurationServiceMock;
#Autowired
private VehicleAuditExecutionService vehicleAuditExecutionService;
#Test
void testExecuteVehicleAudits() throws IOException {
// quite some DB operations for the test setup here
AuditDurationConfigDTO auditDurationConfigDTO = new AuditDurationConfigDTO();
auditDurationConfigDTO.setMaxDuration(deMaxDuration);
Map<String, ScheduledAuditConfigDTO> map = new HashMap<>();
map.put(country, ScheduledAuditConfigDTO.builder()
.groupCalculationEnabled(false)
.build());
when(configurationServiceMock.getAuditDurationConfig(country)).thenReturn(auditDurationConfigDTO);
when(configurationServiceMock.getScheduledAuditConfigurationForCountry(country)).thenReturn(ScheduledAuditConfigDTO.builder()
.groupCalculationEnabled(false)
.sameAuditDurationAllDealersSameGroupId(false)
.build());
vehicleAuditExecutionService.executeVehicleAudits(startDate, country);
verify(publishAuditInterfaceMock).pushExecutions(country, dealerDe1ExportAuditDtoList, auditCategory);
}
#ComponentScan(basePackages = "com.application")
#SpringBootApplication
#PropertySource("classpath:application-test.yml")
static class TestConfiguration {}
}
After the setup, the stubbings are available:
During the test's execution, vehicleAuditExecutionService.executeVehicleAudits(startDate, country) calls the AuditPreparationService, which in turn uses the configurationServiceMock (using #Autowired constructor injection). As expected, the calls gets matched and result set up is returned.
Later, the execution returns to vehicleAuditExecutionService.executeVehicleAudits(startDate, country), where it calls the configurationServiceMock (#Autowired constructor injection, as well) again. But here, the mock's configuration has been changed: the mock's attribute mockitoInterceptor gets replaced by some other instance.
Result: the stubbing is gone and the call returns null - leading to a NPE.
The screenshots were taken using org.springframework.boot:spring-boot-starter-parent:2.7.6, but I've tried that with multiple Spring Boot versions:
2.7.0
2.6.14
2.5.14
2.4.13
2.3.12.RELEASE
Each version has this issue - but I've never seen it in any other test. So I guess, there's something wrong with my test setup - but I cannot spot it.
Any idea, why this is happening?
Thanks a lot - please do not hesitate to ask for any further information, if needed for analysis.
kniffte

An unexpected SQLSelectCountMismatchException occurs

I have been complaining that I must check the Hibernate queries with my eyes without automation. That's why I am fascinated by the library called Hypersistence Utils by vladmihalcea.
But here is the problem. I am unable to use its methods. I use Kotlin Spring Boot.
Below is the source code:
Entity
#Entity
class Book(
#Id
#GeneratedValue(strategy = IDENTITY)
val id: Long = 0L,
)
Repository
interface BookRepository : JpaRepository<Book, Long>
Service
#Service
class DemoService(
private val bookRepository: BookRepository,
) {
#Transactional
fun a() {
bookRepository.findById(0)
}
}
Finally, test code
#SpringBootTest
#TestConstructor(autowireMode = ALL)
internal class DemoServiceTest(
private val demoService: DemoService,
) {
#Test
fun a() {
reset()
demoService.a()
assertSelectCount(1)
}
}
This test gives me a failing result with the message below:
Expected 1 statements but recorded 0 instead!
com.vladmihalcea.sql.exception.SQLSelectCountMismatchException: Expected 1 statements but recorded 0 instead!
What I don't understand is that, the service method a() under test is transactional one but the assertSelectCount() method does not detect the query having been executed.
Here are the logs:
[ Test worker] org.hibernate.SQL : select book0_.id as id1_0_0_ from book book0_ where book0_.id=?
[ Test worker] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [0]
Could you please help me?
Thanks in advance.
It was data proxy that I missed.
Below codes are needed to pass the test method a(). I have found this here.
#Component
class DatasourceProxyBeanPostProcessor : BeanPostProcessor {
override fun postProcessBeforeInitialization(bean: Any, beanName: String): Any {
return bean
}
override fun postProcessAfterInitialization(bean: Any, beanName: String): Any {
return if (bean is DataSource) {
ProxyDataSourceBuilder.create(bean)
.logQueryBySlf4j(INFO)
.multiline()
.countQuery()
.build()
} else bean
}
}
I found a working integration of vladmihalcea's db-util in the github project Ancarian/Facluties in Spring Boot Test. Adding this Component adds the required listener to the datasource used in your Junit Test : DatasourceProxyBeanPostProcessor.java

Mockk "verify" takes up to 6 seconds when verifying method with 3 or more parameters

I use mockk (versions 1.12.0 and 1.12.3) in #SpringBootTest in order to create a spy of some service and verify later that the service's method was (or was not) called. This looks like this:
#SpringBootTest
class MyTests {
#Autowired
private lateinit var someBean: SomeBean
#TestConfiguration
class Config {
#Bean
#Primary
fun someBeanSpy(someBean: SomeBean) = spyk(someBean)
}
#BeforeEach
internal fun setUp() {
clearAllMocks()
}
#ParameterizedTest
#MethodSource("data")
fun `some test`(s: String) {
// prepare data and call controller
verify(exactly = 0) { someBean.foo(any(), any(), any()) } // <- execution of this verify lasts about 6 seconds
// other verify statements last about 200ms
}
companion object {
#JvmStatic
fun data(): Stream<Arguments> = Stream.of(Arguments.of("abc"), Arguments.of("cde"))
}
}
However, the execution of verify method on spy's methods with 3 or more parameters take too long time. Why may I have such behavior?
The same works fine with Mockito, but I don't want to use it with Kotlin because than I can't use just regular Mockito#any with non-nullable types.
Also, when I reduced the SomeBean#foo method's number of parameters from 3 to 2, verify executed just as normal, about 200ms.

Cant inject Spring dependencies to both Spock and Spring at same time

I'm having troubles with setting up my tests. I'm using latest version of SpringBoot and Spock Framework. First of all, I'm not configuring my beans "traditional" way. All my classes in package except Facade are package-scoped. I'm not using #Component, #Service, etc.
The only one class Im injecting is Repository. Let me show you my Configuration class
#Configuration
class SurveyConfiguration {
#Bean
SurveyFacade surveyFacade(SurveyRepository surveyRepository) {
ConversionUtils conversionUtils = new ConversionUtils();
SurveyValidator surveyValidator = new SurveyValidator();
SurveyCreator surveyCreator = new SurveyCreator(surveyRepository, conversionUtils, surveyValidator);
return new SurveyFacade(surveyCreator);
}
}
It works fine, I'v tested all scenarios manually (sending POST to certain endpoint). Let me show you example method from SurveyCreator class I want to test.
SurveyDTO createSurvey(final SurveyDTO surveyDTO) throws ValidationException, PersistenceException {
Survey survey = conversionUtils.surveyToEntity(surveyDTO);
surveyValidator.validate(survey);
Optional<Survey> savedInstance = Optional.ofNullable(surveyRepository.save(survey)); //Will throw NullPtr
return savedInstance.map(conversionUtils::surveyToDTO)
.orElseThrow(PersistenceException::new);
}
Like I said, during runtime it works fine. So lets move on to tests
#SpringBootTest
class SurveyFacadeTest extends Specification {
#Autowired
private SurveyRepository surveyRepository
private SurveyFacade surveyFacade = new SurveyConfiguration().surveyFacade(this.surveyRepository)
def "should inject beans"() {
expect:
surveyRepository != null
surveyFacade != null
}
def "should create survey and return id"() {
given:
Long id
when:
id = surveyFacade.createSurvey(SampleSurveys.validSurvey())
then:
id != surveyFacade
}
}
First test passes, so I understand I got everything ok for tests. But, I'm getting NullPointer in my java code in method I posted above. Looks like SurveyRepository isnt injected into java code during tests, because thats the one that causes this exception... Any ideas how to work around that, to have my Repository injected in both Spring application and Spock tests?
If there are no reasons against, I recommend you to run the test on the "underlying bean" (and not a manually created instance):
#Autowired
private SurveyFacade surveyFacade;

Is it still true that SpringJUnit4ClassRunner can't reliably call DisposableBean.destroy after tests

According to: https://jira.spring.io/browse/SPR-4103
SpringJUnit4ClassRunner won't always called DisposableBean.destroy after tests (*facepalm*!) -- due to implementation problems with JUnit.
Is this still true?
I'm creating tests in scala like so:
#RunWith(classOf[SpringJUnit4ClassRunner])
#WebAppConfiguration
#ContextConfiguration(classes = Array(classOf[Service1Config]))
class Service1Test {
#Test
def test1(): Unit = {
}
}
#RunWith(classOf[SpringJUnit4ClassRunner])
#WebAppConfiguration
#ContextConfiguration(classes = Array(classOf[Service2Config]))
class Service2Test {
#Test
def test2(): Unit = {
}
}
and I'm finding that the destroy method of beans in Service1Config aren't destroyed when Service2Test is executed.
I've found many articles recommending adding a #After to shut down the context explicitly. This sounds like an error waiting to happen (since if you forget to add an #After cleanup in one test, the next test class will fail and you will have no idea why).
If this still can't be done with SpringJUnit4ClassRunner/JUnit, is there a test framework that will automatically call the context cleanup after each test?
Try adding to the class
#DirtiesContext(classMode=ClassMode.AFTER_CLASS)
class Service1Test {
That will do the trick.

Resources