Search code examples
unit-testinggroovyspock

How to test interactions on delegate called by invoking class?


I have the following classes shown below, legacy code. The goal I want to achieve is to ensure that the delegate method processUser is called with the user data passed. Second, I also want to ensure that the passed in Registration object's doRegister is called. My attemot is shown below for the delegate, but the test does not pass as it says, Too few invocations. I am using Groovy spock for testing version 1.2

class Invoker {
    Delegate delegate;

    Invoker(Delegate delegate) {
        this.delegate = delegate;
    }

    void invoke(UserData user) {
        delegate.processUser(user);
    }
}
class Delegate {
    private RegistrationService service;

    Delegate (RegistrationService r) {
        this.service = r;
    }

    void processUser(UserData data) {
        service.doRegistration(data);
    }
}
class DelegateSpec extends Specification {
     Delegate delegate
     RegistrationService registration
     Invoker invoker

     def setup() {
         registration = Mock()
         delegate = new Delegate(registration)
         Invoker invoker = new Invoker(delegate)
     }

     def "Invoker should invoke delegate passed to it"() {
         given:
         UserData u = ....
         when:
         invoker.invoke(u)
         then:
         1* delegate.processUser(u) 
    }
}

Solution

  • First let me provide a fully consistent set of classes in order to be able to compile the application code:

    package de.scrum_master.stackoverflow.q59366025;
    
    public class UserData {}
    
    package de.scrum_master.stackoverflow.q59366025;
    
    public class RegistrationService {
      public void doRegistration(UserData data) {}
    }
    
    package de.scrum_master.stackoverflow.q59366025;
    
    class Delegate {
      private RegistrationService service;
    
      Delegate (RegistrationService r) {
        this.service = r;
      }
    
      void processUser(UserData data) {
        service.doRegistration(data);
      }
    }
    
    package de.scrum_master.stackoverflow.q59366025;
    
    class Invoker {
      Delegate delegate;
    
      Invoker(Delegate delegate) {
        this.delegate = delegate;
      }
    
      void invoke(UserData user) {
        delegate.processUser(user);
      }
    }
    

    Now as for your test, you are making it more complicated than necessary and there is also a logical error:

    • If the Delegate is not mock or spy, you cannot check interactions like 1 * on it.
    • So just make it a mock, then you also do not need to inject its RegistrationService dependency anymore - which is the whole point of creating a mock.
    package de.scrum_master.stackoverflow.q59366025
    
    import spock.lang.Specification
    
    class DelegateSpec extends Specification {
      def delegate = Mock(Delegate)
      def invoker = new Invoker(delegate)
    
      def "Invoker should invoke delegate passed to it"() {
        given:
        def userData = new UserData()
        when:
        invoker.invoke(userData)
        then:
        1 * delegate.processUser(userData)
      }
    }