Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I validate a Guice scope's usage in tests?

I have some tests that I would like to have fail if certain Guice scopes are used incorrectly. For example, a @Singleton should not have any @RequestScoped or @TestScoped dependencies (Provider<>s are okay, of course).

In production, this is partially solved because eagerly-bound singletons will be constructed before the scope is entered, resulting in OutOfScopeExceptions. But in development, the singleton will be created lazily while inside the scope, and no problems are evident.

Judging by these two open issues, it seems like there is no easy, built-in way to do this. Can I achieve this using the SPI? I tried using a TypeListener but it's not clear how to get the dependencies of a given type.

like image 633
Tavian Barnes Avatar asked Aug 27 '13 19:08

Tavian Barnes


People also ask

What does @inject mean Guice?

Note that the only Guice-specific code in the above is the @Inject annotation. This annotation marks an injection point. Guice will attempt to reconcile the dependencies implied by the annotated constructor, method, or field.

How does Guice injector work?

Using GuiceIn each of your constructors that need to have something injected in them, you just add an @Inject annotation and that tells Guice to do it's thing. Guice figures out how to give you an Emailer based on the type. If it's a simple object, it'll instantiate it and pass it in.

What is binding in Guice?

A binding is an object that corresponds to an entry in the Guice map. You add new entries into the Guice map by creating bindings.

What is module in Google Guice?

Interface Module A module contributes configuration information, typically interface bindings, which will be used to create an Injector . A Guice-based application is ultimately composed of little more than a set of Module s and some bootstrapping code.


1 Answers

this is not a trivial problem, but definitely it is a good question! There could be a tester for scope binding problems you mentioned. I think i could make a Junit runner to generate warning with wrong binding practice. I will update this post later with it.

For now there is a example how to get binding scopes.

Module

public class ScopeTestModel extends ServletModule {

  @Override
  protected void configureServlets() {
    super
        .configureServlets();
    bind(Key.get(Object.class, Names.named("REQ1"))).to(Object.class).in(ServletScopes.REQUEST);
    bind(Key.get(Object.class, Names.named("REQ2"))).to(RequestScopedObject.class);

    bind(Key.get(Object.class, Names.named("SINGLETON1"))).to(Object.class).asEagerSingleton();
    bind(Key.get(Object.class, Names.named("SINGLETON2"))).to(Object.class).in(Scopes.SINGLETON);
    bind(Key.get(Object.class, Names.named("SINGLETON3"))).to(SingletonScopedObject.class);

    bind(Key.get(Object.class, Names.named("SESS1"))).to(Object.class).in(ServletScopes.SESSION);
    bind(Key.get(Object.class, Names.named("SESS2"))).to(SessionScopedObject.class);
  }
}

TestCase

public class TestScopeBinding {

  private Injector injector = Guice.createInjector(new ScopeTestModel());

  @Test
  public void testRequestScope() throws Exception {
    Binding<Object> req1 = injector.getBinding(Key.get(Object.class, Names.named("REQ1")));
    Binding<Object> req2 = injector.getBinding(Key.get(Object.class, Names.named("REQ2")));

    Scope scope1 = getScopeInstanceOrNull(req1);
    Scope scope2 = getScopeInstanceOrNull(req2);

    Assert.assertEquals(ServletScopes.REQUEST,scope1);
    Assert.assertEquals(ServletScopes.REQUEST,scope2);
  }

  @Test
  public void testSessionScope() throws Exception {
    injector.getAllBindings();
    Binding<Object> sess1 = injector.getBinding(Key.get(Object.class, Names.named("SESS1")));
    Binding<Object> sess2 = injector.getBinding(Key.get(Object.class, Names.named("SESS2")));

    Scope scope1 = getScopeInstanceOrNull(sess1);
    Scope scope2 = getScopeInstanceOrNull(sess2);

    Assert.assertEquals(ServletScopes.SESSION,scope1);
    Assert.assertEquals(ServletScopes.SESSION,scope2);
  }

  @Test
  public void testSingletonScope() throws Exception {
    injector.getAllBindings();
    Binding<Object> sng1 = injector.getBinding(Key.get(Object.class, Names.named("SINGLETON1")));
    Binding<Object> sng2 = injector.getBinding(Key.get(Object.class, Names.named("SINGLETON2")));
    Binding<Object> sng3 = injector.getBinding(Key.get(Object.class, Names.named("SINGLETON3")));

    Scope scope1 = getScopeInstanceOrNull(sng1);
    Scope scope2 = getScopeInstanceOrNull(sng2);
    Scope scope3 = getScopeInstanceOrNull(sng3);

    Assert.assertEquals(Scopes.SINGLETON,scope1);
    Assert.assertEquals(Scopes.SINGLETON,scope2);
    Assert.assertEquals(Scopes.SINGLETON,scope3);
  }

  private Scope getScopeInstanceOrNull(final Binding<?> binding) {
    return binding.acceptScopingVisitor(new DefaultBindingScopingVisitor<Scope>() {

      @Override
      public Scope visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
        throw new RuntimeException(String.format("I don't know how to handle the scopeAnnotation: %s",scopeAnnotation.getCanonicalName()));
      }

      @Override
      public Scope visitNoScoping() {
          if(binding instanceof LinkedKeyBinding) {
            Binding<?> childBinding = injector.getBinding(((LinkedKeyBinding)binding).getLinkedKey());
            return getScopeInstanceOrNull(childBinding);
          }
        return null;
      }

      @Override
      public Scope visitEagerSingleton() {
        return Scopes.SINGLETON;
      }

      public Scope visitScope(Scope scope) {
        return scope;
      }
    });
  }
}

Scoped objects

@RequestScoped
public class RequestScopedObject extends Object {

}

@SessionScoped
public class SessionScopedObject extends Object {

}

@Singleton
public class SingletonScopedObject extends Object {

}
like image 60
Milan Baran Avatar answered Nov 03 '22 17:11

Milan Baran