Search code examples
unit-testinggroovyspock

Mock Object returned has all null fields causing test to fail


I have a simple class that uses a service called ReportingService. It gets injected into the class and I am verifying that it is called and with the expected arguments. The mock, returns the ReportSummary object i want to assert in the tests, by ensuring reportType and the other values meet the expectations. Unfortunately, the ReportSummary fields are all null, what am i doing wrong?

class ReportSummary {
   String reportType
   Date createdTime
   String reportedById 
} 
class ReportingService {
   ReportSummary updateReport(Report reportData) {
       ...
   }
}

This Service is injected into a class PricingService like this:

   class PricingService {
       @Autowired
       ReportingService reportingService

       ReportingSummary updatePricingDetails( ReportData data) {
           reportingService.updateReport(data)
       }
  }

The class is used in the service like this

class PricingServiceSpec extends Specification {

   ReportingService reportingService = Mock()
   PricingService pricingService = new PricingService( reportingService)
   ReportData  data = new ReportData(........)       

   def "Should return summary report data" (){
     given:       
     Date today = new Date (System.currentMillis())

     ReportSummary summary = new ReportSummary(reportType: 'Pricing',createdTime:           
     today, reportedById: 'GXT111111345')
  
     when:
  
     ReportSummary response = pricingService.updatePricingDetails(data)

     then:
     1 * reportingService.updateReport( data : {
   
     assert it.pricingDescription == 'Pricing for Produce'
  
     }) >> summary
  
     response.reportType == 'Pricing'  // The reportType is null why?
     response.reportedById == 'GXT111111345'   // The reportById is null, why? The 
     0 * _       
    )       
   }
 }

Solution

  • Here is some example code which at least compiles and kind of emulates your situation, even though it returns some dummy data (but at least different from the stub result you want to test):

    package de.scrum_master.stackoverflow.q67912725
    
    class ReportData {
      String pricingDescription;
    }
    
    package de.scrum_master.stackoverflow.q67912725
    
    class ReportSummary {
      String reportType
      Date createdTime
      String reportedById
    }
    
    package de.scrum_master.stackoverflow.q67912725
    
    class ReportingService {
      ReportSummary updateReport(ReportData reportData) {
        return new ReportSummary(
          reportType: "real report",
          createdTime: new Date(),
          reportedById: "me"
        )
      }
    }
    
    package de.scrum_master.stackoverflow.q67912725
    
    import org.springframework.beans.factory.annotation.Autowired
    
    class PricingService {
      @Autowired
      ReportingService reportingService
    
      ReportSummary updatePricingDetails(ReportData data) {
        reportingService.updateReport(data)
      }
    }
    

    Here is a passing test. Your basic problem was that you tried to use a method parameter name in a method constraint, i.e. in your original code the data : part before the asserting closure. Method arguments are matched by type and position, not by parameter name. Omit that, and you are fine.

    BTW, you also should not use assert in the method parameter constraint, but a simple boolean expression. A failed assertion would make the test fail, which is not what you want. You simply want to match a parameter value.

    Fixing the test was not so difficult for me. It took me 10 times longer to actually make this mess compile and do something logical in order to even begin to analyse the test.

    package de.scrum_master.stackoverflow.q67912725
    
    import spock.lang.Specification
    
    class PricingServiceTest extends Specification {
      def reportingService = Mock(ReportingService)
      def pricingService = new PricingService(reportingService: reportingService)
      def reportData = new ReportData(pricingDescription: 'Pricing for Produce')
    
      def "Should return summary report data"() {
        given:
        ReportSummary stubbedSummary = new ReportSummary(
          reportType: 'Pricing',
          createdTime: new Date(),
          reportedById: 'GXT111111345'
        )
    
        when:
        ReportSummary reportSummary = pricingService.updatePricingDetails(reportData)
    
        then:
        1 * reportingService.updateReport(
          { it.pricingDescription == 'Pricing for Produce' }
        ) >> stubbedSummary
        reportSummary.reportType == stubbedSummary.reportType
        reportSummary.reportedById == stubbedSummary.reportedById
        0 * _
      }
    }