Class SpiesHook

  • All Implemented Interfaces:
    GuiceyConfigurationHook

    public class SpiesHook
    extends java.lang.Object
    implements GuiceyConfigurationHook
    Replace any guice service with mockito spy. The difference with mock: spy wraps around the real service(!) and could be used to validate called service methods (verify incoming parameters and output value).

    Important: requires mockito dependency!

    In contrast to mocks and stubs, spies work with guice AOP: all calls to service are intercepted and passed through the spy object (as "proxy"). That also means that all aop, applied to the original bean, would work (in contrast to mocks).

    As spy requires real bean instance - spy object is created just after injector creation (and AOP interceptor redirects into it (then real bean called)). Spy object is different instance as injected bean (@Inject SpiedService) because injected bean would be a proxy, handling guice AOP.

    Calling bean methods directly on spy is completely normal (guice bean just redirects calls to spy object)!

    Usage example:

    
         SpiesHook hook = new SpiesHook();
         SpyProxy<Service> proxy = hook.spy(Service.class)
         // actual spy object can be obtained only after guice application startup
         Service spy = proxy.getSpy()
         doReturn(12).when(spy).foo();
     

    Alternatively, provider might be used instead of proxy type (for simplicity): Provider<Service> provider = hook.spy(Service.class)

    Spy CAN'T be initialized manually. Use MocksHook or StubsHook instead for manual spy objects initialization. Such manual initialization could be required for spies created from abstract classes (or multiple interfaces): in this case actual bean instance is not required, and so mocks support could be used instead: AbstractService spy = mocksHook.mock(AbstractService.class, Mockito.spy(AbstractService.class)).

    Actual spy object instance is created only on first bean access (after or in time of application startup). Normally, it is ok to wait for application startup, configure spy object and then run tests methods (using spy). But if spied bean is involved in application startup (called by some managed objects) then the only way to configure it is to apply modification just after spy instance creation: hook.spy(Service.class).withInitializer(spy -> { doReturn(12).when(spy).foo() }).

    Since:
    29.04.2025
    • Constructor Summary

      Constructors 
      Constructor Description
      SpiesHook()  
    • Method Summary

      All Methods Instance Methods Concrete Methods 
      Modifier and Type Method Description
      void configure​(GuiceBundle.Builder builder)
      Configuration is applied just after manual configuration (through bundle's builder in application class).
      <T> T getSpy​(java.lang.Class<T> type)  
      void resetSpies()
      Reset all registered spies.
      <T> SpyProxy<T> spy​(java.lang.Class<T> type)
      Request wrapping target bean with a spy.
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Constructor Detail

      • SpiesHook

        public SpiesHook()
    • Method Detail

      • spy

        public <T> SpyProxy<T> spy​(java.lang.Class<T> type)
        Request wrapping target bean with a spy.

        As spy object requires bean instance, then spy could be created only during injector startup. Returned proxy must be used to get an actual spy object after application startup (for configuration before test logic execution).

        Returned proxy instance could be used for startup initializer registration: hook.spy(Service.class).withInitializer(...) (required ONLY in cases when spy must be used during application startup and so can't be configured after application startup.

        Returned proxy also implements Provider interface, so provider could be used instead of proxy type: Provider<Service> provider = hook.spy(Service.class)

        For spies targeting guice beans registered by instance use mocks (MocksHook) because in this case bean instance is not required.

        Type Parameters:
        T - bean type
        Parameters:
        type - bean type
        Returns:
        spy proxy instance
      • getSpy

        public <T> T getSpy​(java.lang.Class<T> type)
        Type Parameters:
        T - bean type
        Parameters:
        type - bean type
        Returns:
        mockito spy object (not proxy!)
        Throws:
        java.lang.IllegalStateException - if spy for bean is not registered
      • resetSpies

        public void resetSpies()
        Reset all registered spies.