Search code examples
springunit-testingkotlinspock

IllegalStateException: must not be null of Spock unit test with Kotlin business logic


I try to get a MongoTemplate (Spring Data) worked with my Spock spec test. I use Kotlin as language for the business logic.

Please see my specification logic:

@SpringBootTest
class BookingServiceSpec extends Specification {

    BookingService bookingService;
    BookingEntity bookingEntity
    CustomerEntity customerEntity
    MongoTemplate mongoTemplate;

    def setup() {
        bookingEntity = GroovyMock(BookingEntity)
        customerEntity = GroovyMock(CustomerEntity)
        mongoTemplate = Mock()
        bookingService = new BookingService(mongoTemplate)

        customerEntity.getEmail() >> "test@test.com"
        mongoTemplate.find(!null, !null, !null) >> List.of(bookingEntity)
    }

    def "should return a list of bookings if asked for bookings for a customer"() {
        when: "booking service is used to find bookings for a given customer"
        List<BookingEntity> bookings = bookingService.getBookings(customerEntity)
        then: "it should call the find method of the mongo template and return a list of booking entities"
        1 * mongoTemplate.find(!null, !null, !null)
    }
}

Running this code throws an IllegalStateException with detail:

java.lang.IllegalStateException: mongoTemplate.find(query…::class.java, "bookings") must not be null

    at com.nomadsolutions.areavr.booking.BookingService.getBookings(BookingService.kt:22)
    at com.nomadsolutions.areavr.booking.BookingServiceSpec.should return a list of bookings if asked for bookings for a customer(BookingServiceSpec.groovy:37)

The entity data classes are defined like this:

data class CustomerEntity(val surname: String, val name: String, val email: String)
data class BookingEntity(val customerEntity: CustomerEntity, val date: Date)

And here is the business logic:

@Service
class BookingService(var mongoTemplate: MongoTemplate) {

    fun addBooking(booking: BookingEntity) {
        mongoTemplate.insert(booking)
    }

    fun getBookings(customerEntity: CustomerEntity): List<BookingEntity> {
        val query = Query()
        query.addCriteria(Criteria.where("customer.email").`is`(customerEntity.email))
        return mongoTemplate.find(query, BookingEntity::class.java, "bookings")
    }

}

I found out that the stubbing for customerEntity is not working properly since customerEntity.email is returning null in the logic while debugging via test run.

I'd love to continue with Spock but it seems to block me from testing fast since I have to care about things like this.


Solution

  • Remove this line from setup() method:

        mongoTemplate.find(!null, !null, !null) >> List.of(bookingEntity)
    

    And change the interaction test in the then section of the test case in this way:

    then: 'it should call the find method of the mongo template and return a list of booking entities'
    1 * mongoTemplate.find(!null, !null, !null) >> [bookingEntity]
    

    It is because when you are mocking and stubbing the same method call (mongoTemplate.find() in your case) it should happen in the same interaction.

    You can read more about that in the documentation.