/* * Copyright (C) 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.inject.throwingproviders; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.inject.AbstractModule; import com.google.inject.BindingAnnotation; import com.google.inject.CreationException; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; import com.google.inject.internal.util.Classes; import com.google.inject.name.Named; import com.google.inject.name.Names; import com.google.inject.spi.Dependency; import com.google.inject.spi.HasDependencies; import com.google.inject.spi.Message; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.rmi.AccessException; import java.rmi.RemoteException; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.TooManyListenersException; import junit.framework.TestCase; /** * @author jmourits@google.com (Jerome Mourits) * @author jessewilson@google.com (Jesse Wilson) */ @SuppressWarnings("deprecation") public class ThrowingProviderTest extends TestCase { @Target(METHOD) @Retention(RUNTIME) @BindingAnnotation @interface NotExceptionScoping {}; private final TypeLiteral<RemoteProvider<String>> remoteProviderOfString = new TypeLiteral<RemoteProvider<String>>() {}; private final MockRemoteProvider<String> mockRemoteProvider = new MockRemoteProvider<String>(); private final TestScope testScope = new TestScope(); private Injector bindInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { ThrowingProviderBinder.create(binder()) .bind(RemoteProvider.class, String.class) .to(mockRemoteProvider) .in(testScope); ThrowingProviderBinder.create(binder()) .bind(RemoteProvider.class, String.class) .annotatedWith(NotExceptionScoping.class) .scopeExceptions(false) .to(mockRemoteProvider) .in(testScope); } }); private Injector providesInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); bindScope(TestScope.Scoped.class, testScope); } @SuppressWarnings("unused") @CheckedProvides(RemoteProvider.class) @TestScope.Scoped String throwOrGet() throws RemoteException { return mockRemoteProvider.get(); } @SuppressWarnings("unused") @CheckedProvides(value = RemoteProvider.class, scopeExceptions = false) @NotExceptionScoping @TestScope.Scoped String notExceptionScopingThrowOrGet() throws RemoteException { return mockRemoteProvider.get(); } }); public void testExceptionsThrown_Bind() { tExceptionsThrown(bindInjector); } public void testExceptionsThrown_Provides() { tExceptionsThrown(providesInjector); } private void tExceptionsThrown(Injector injector) { RemoteProvider<String> remoteProvider = injector.getInstance(Key.get(remoteProviderOfString)); mockRemoteProvider.throwOnNextGet("kaboom!"); try { remoteProvider.get(); fail(); } catch (RemoteException expected) { assertEquals("kaboom!", expected.getMessage()); } } public void testValuesScoped_Bind() throws RemoteException { tValuesScoped(bindInjector, null); } public void testValuesScoped_Provides() throws RemoteException { tValuesScoped(providesInjector, null); } public void testValuesScopedWhenNotExceptionScoping_Bind() throws RemoteException { tValuesScoped(bindInjector, NotExceptionScoping.class); } public void testValuesScopedWhenNotExceptionScoping_Provides() throws RemoteException { tValuesScoped(providesInjector, NotExceptionScoping.class); } private void tValuesScoped(Injector injector, Class<? extends Annotation> annotation) throws RemoteException { Key<RemoteProvider<String>> key = annotation != null ? Key.get(remoteProviderOfString, annotation) : Key.get(remoteProviderOfString); RemoteProvider<String> remoteProvider = injector.getInstance(key); mockRemoteProvider.setNextToReturn("A"); assertEquals("A", remoteProvider.get()); mockRemoteProvider.setNextToReturn("B"); assertEquals("A", remoteProvider.get()); testScope.beginNewScope(); assertEquals("B", remoteProvider.get()); } public void testExceptionsScoped_Bind() { tExceptionsScoped(bindInjector); } public void testExceptionsScoped_Provides() { tExceptionsScoped(providesInjector); } private void tExceptionsScoped(Injector injector) { RemoteProvider<String> remoteProvider = injector.getInstance(Key.get(remoteProviderOfString)); mockRemoteProvider.throwOnNextGet("A"); try { remoteProvider.get(); fail(); } catch (RemoteException expected) { assertEquals("A", expected.getMessage()); } mockRemoteProvider.throwOnNextGet("B"); try { remoteProvider.get(); fail(); } catch (RemoteException expected) { assertEquals("A", expected.getMessage()); } } public void testExceptionsNotScopedWhenNotExceptionScoping_Bind() { tExceptionsNotScopedWhenNotExceptionScoping(bindInjector); } public void testExceptionsNotScopedWhenNotExceptionScoping_Provides() { tExceptionsNotScopedWhenNotExceptionScoping(providesInjector); } private void tExceptionsNotScopedWhenNotExceptionScoping(Injector injector) { RemoteProvider<String> remoteProvider = injector.getInstance(Key.get(remoteProviderOfString, NotExceptionScoping.class)); mockRemoteProvider.throwOnNextGet("A"); try { remoteProvider.get(); fail(); } catch (RemoteException expected) { assertEquals("A", expected.getMessage()); } mockRemoteProvider.throwOnNextGet("B"); try { remoteProvider.get(); fail(); } catch (RemoteException expected) { assertEquals("B", expected.getMessage()); } } public void testAnnotations_Bind() throws RemoteException { final MockRemoteProvider<String> mockRemoteProviderA = new MockRemoteProvider<String>(); final MockRemoteProvider<String> mockRemoteProviderB = new MockRemoteProvider<String>(); bindInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { ThrowingProviderBinder.create(binder()) .bind(RemoteProvider.class, String.class) .annotatedWith(Names.named("a")) .to(mockRemoteProviderA); ThrowingProviderBinder.create(binder()) .bind(RemoteProvider.class, String.class) .to(mockRemoteProviderB); } }); tAnnotations(bindInjector, mockRemoteProviderA, mockRemoteProviderB); } public void testAnnotations_Provides() throws RemoteException { final MockRemoteProvider<String> mockRemoteProviderA = new MockRemoteProvider<String>(); final MockRemoteProvider<String> mockRemoteProviderB = new MockRemoteProvider<String>(); providesInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(RemoteProvider.class) @Named("a") String throwOrGet() throws RemoteException { return mockRemoteProviderA.get(); } @SuppressWarnings("unused") @CheckedProvides(RemoteProvider.class) String throwOrGet2() throws RemoteException { return mockRemoteProviderB.get(); } }); tAnnotations(providesInjector, mockRemoteProviderA, mockRemoteProviderB); } private void tAnnotations( Injector injector, MockRemoteProvider<String> mockA, MockRemoteProvider<String> mockB) throws RemoteException { mockA.setNextToReturn("A"); mockB.setNextToReturn("B"); assertEquals( "A", injector.getInstance(Key.get(remoteProviderOfString, Names.named("a"))).get()); assertEquals("B", injector.getInstance(Key.get(remoteProviderOfString)).get()); } public void testUndeclaredExceptions_Bind() throws RemoteException { tUndeclaredExceptions(bindInjector); } public void testUndeclaredExceptions_Provides() throws RemoteException { tUndeclaredExceptions(providesInjector); } private void tUndeclaredExceptions(Injector injector) throws RemoteException { RemoteProvider<String> remoteProvider = injector.getInstance(Key.get(remoteProviderOfString)); mockRemoteProvider.throwOnNextGet(new IndexOutOfBoundsException("A")); try { remoteProvider.get(); fail(); } catch (RuntimeException e) { assertEquals("A", e.getCause().getMessage()); } // undeclared exceptions shouldn't be scoped mockRemoteProvider.throwOnNextGet(new IndexOutOfBoundsException("B")); try { remoteProvider.get(); fail(); } catch (RuntimeException e) { assertEquals("B", e.getCause().getMessage()); } } public void testThrowingProviderSubclassing() throws RemoteException { final SubMockRemoteProvider aProvider = new SubMockRemoteProvider(); aProvider.setNextToReturn("A"); bindInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { ThrowingProviderBinder.create(binder()) .bind(RemoteProvider.class, String.class) .to(aProvider); } }); assertEquals("A", bindInjector.getInstance(Key.get(remoteProviderOfString)).get()); } static class SubMockRemoteProvider extends MockRemoteProvider<String> {} public void testBindingToNonInterfaceType_Bind() throws RemoteException { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { ThrowingProviderBinder.create(binder()) .bind(MockRemoteProvider.class, String.class) .to(mockRemoteProvider); } }); fail(); } catch (CreationException expected) { assertEquals( MockRemoteProvider.class.getName() + " must be an interface", Iterables.getOnlyElement(expected.getErrorMessages()).getMessage()); } } public void testBindingToNonInterfaceType_Provides() throws RemoteException { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(MockRemoteProvider.class) String foo() { return null; } }); fail(); } catch (CreationException expected) { assertEquals( MockRemoteProvider.class.getName() + " must be an interface", Iterables.getOnlyElement(expected.getErrorMessages()).getMessage()); } } public void testBindingToSubSubInterface_Bind() throws RemoteException { try { bindInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { ThrowingProviderBinder.create(binder()) .bind(SubRemoteProvider.class, String.class); } }); fail(); } catch (CreationException expected) { assertEquals( SubRemoteProvider.class.getName() + " must extend CheckedProvider (and only CheckedProvider)", Iterables.getOnlyElement(expected.getErrorMessages()).getMessage()); } } public void testBindingToSubSubInterface_Provides() throws RemoteException { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(SubRemoteProvider.class) String foo() { return null; } }); fail(); } catch (CreationException expected) { assertEquals( SubRemoteProvider.class.getName() + " must extend CheckedProvider (and only CheckedProvider)", Iterables.getOnlyElement(expected.getErrorMessages()).getMessage()); } } interface SubRemoteProvider extends RemoteProvider<String> {} public void testBindingToInterfaceWithExtraMethod_Bind() throws RemoteException { try { bindInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { ThrowingProviderBinder.create(binder()) .bind(RemoteProviderWithExtraMethod.class, String.class); } }); fail(); } catch (CreationException expected) { assertEquals( RemoteProviderWithExtraMethod.class.getName() + " may not declare any new methods, but declared " + RemoteProviderWithExtraMethod.class.getDeclaredMethods()[0].toGenericString(), Iterables.getOnlyElement(expected.getErrorMessages()).getMessage()); } } public void testBindingToInterfaceWithExtraMethod_Provides() throws RemoteException { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(RemoteProviderWithExtraMethod.class) String foo() { return null; } }); fail(); } catch (CreationException expected) { assertEquals( RemoteProviderWithExtraMethod.class.getName() + " may not declare any new methods, but declared " + RemoteProviderWithExtraMethod.class.getDeclaredMethods()[0].toGenericString(), Iterables.getOnlyElement(expected.getErrorMessages()).getMessage()); } } public void testDependencies_Bind() { bindInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { bind(String.class).toInstance("Foo"); bind(Integer.class).toInstance(5); bind(Double.class).toInstance(5d); bind(Long.class).toInstance(5L); ThrowingProviderBinder.create(binder()) .bind(RemoteProvider.class, String.class) .to(DependentRemoteProvider.class); } }); HasDependencies hasDependencies = (HasDependencies) bindInjector.getBinding(Key.get(remoteProviderOfString)); hasDependencies = (HasDependencies) bindInjector.getBinding( Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey()); // Make sure that that is dependent on DependentRemoteProvider. assertEquals( Dependency.get(Key.get(DependentRemoteProvider.class)), Iterables.getOnlyElement(hasDependencies.getDependencies())); // And make sure DependentRemoteProvider has the proper dependencies. hasDependencies = (HasDependencies) bindInjector.getBinding(DependentRemoteProvider.class); Set<Key<?>> dependencyKeys = ImmutableSet.copyOf( Iterables.transform( hasDependencies.getDependencies(), new Function<Dependency<?>, Key<?>>() { @Override public Key<?> apply(Dependency<?> from) { return from.getKey(); } })); assertEquals( ImmutableSet.<Key<?>>of( Key.get(String.class), Key.get(Integer.class), Key.get(Long.class), Key.get(Double.class)), dependencyKeys); } public void testDependencies_Provides() { providesInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { bind(String.class).toInstance("Foo"); bind(Integer.class).toInstance(5); bind(Double.class).toInstance(5d); bind(Long.class).toInstance(5L); install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(RemoteProvider.class) String foo(String s, Integer i, Double d, Long l) { return null; } }); HasDependencies hasDependencies = (HasDependencies) providesInjector.getBinding(Key.get(remoteProviderOfString)); // RemoteProvider<String> is dependent on the provider method.. hasDependencies = (HasDependencies) providesInjector.getBinding( Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey()); // And the provider method has our real dependencies.. hasDependencies = (HasDependencies) providesInjector.getBinding( Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey()); Set<Key<?>> dependencyKeys = ImmutableSet.copyOf( Iterables.transform( hasDependencies.getDependencies(), new Function<Dependency<?>, Key<?>>() { @Override public Key<?> apply(Dependency<?> from) { return from.getKey(); } })); assertEquals( ImmutableSet.<Key<?>>of( Key.get(String.class), Key.get(Integer.class), Key.get(Long.class), Key.get(Double.class)), dependencyKeys); } interface RemoteProviderWithExtraMethod<T> extends ThrowingProvider<T, RemoteException> { T get(T defaultValue) throws RemoteException; } interface RemoteProvider<T> extends ThrowingProvider<T, RemoteException> {} static class DependentRemoteProvider<T> implements RemoteProvider<T> { @Inject double foo; @Inject public DependentRemoteProvider(String foo, int bar) {} @Inject void initialize(long foo) {} @Override public T get() throws RemoteException { return null; } } static class MockRemoteProvider<T> implements RemoteProvider<T> { Exception nextToThrow; T nextToReturn; public void throwOnNextGet(String message) { throwOnNextGet(new RemoteException(message)); } public void throwOnNextGet(Exception nextToThrow) { this.nextToThrow = nextToThrow; } public void setNextToReturn(T nextToReturn) { this.nextToReturn = nextToReturn; } @Override public T get() throws RemoteException { if (nextToThrow instanceof RemoteException) { throw (RemoteException) nextToThrow; } else if (nextToThrow instanceof RuntimeException) { throw (RuntimeException) nextToThrow; } else if (nextToThrow == null) { return nextToReturn; } else { throw new AssertionError("nextToThrow must be a runtime or remote exception"); } } } public void testBindingToInterfaceWithBoundValueType_Bind() throws RemoteException { bindInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { ThrowingProviderBinder.create(binder()) .bind(StringRemoteProvider.class, String.class) .to( new StringRemoteProvider() { @Override public String get() throws RemoteException { return "A"; } }); } }); assertEquals("A", bindInjector.getInstance(StringRemoteProvider.class).get()); } public void testBindingToInterfaceWithBoundValueType_Provides() throws RemoteException { providesInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(StringRemoteProvider.class) String foo() throws RemoteException { return "A"; } }); assertEquals("A", providesInjector.getInstance(StringRemoteProvider.class).get()); } interface StringRemoteProvider extends ThrowingProvider<String, RemoteException> {} public void testBindingToInterfaceWithGeneric_Bind() throws RemoteException { bindInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { ThrowingProviderBinder.create(binder()) .bind(RemoteProvider.class, new TypeLiteral<List<String>>() {}.getType()) .to( new RemoteProvider<List<String>>() { @Override public List<String> get() throws RemoteException { return Arrays.asList("A", "B"); } }); } }); Key<RemoteProvider<List<String>>> key = Key.get(new TypeLiteral<RemoteProvider<List<String>>>() {}); assertEquals(Arrays.asList("A", "B"), bindInjector.getInstance(key).get()); } public void testBindingToInterfaceWithGeneric_Provides() throws RemoteException { providesInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(RemoteProvider.class) List<String> foo() throws RemoteException { return Arrays.asList("A", "B"); } }); Key<RemoteProvider<List<String>>> key = Key.get(new TypeLiteral<RemoteProvider<List<String>>>() {}); assertEquals(Arrays.asList("A", "B"), providesInjector.getInstance(key).get()); } public void testProviderMethodWithWrongException() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(RemoteProvider.class) String foo() throws InterruptedException { return null; } }); fail(); } catch (CreationException ce) { assertEquals( InterruptedException.class.getName() + " is not compatible with the exceptions ([" + RemoteException.class + "]) declared in the CheckedProvider interface (" + RemoteProvider.class.getName() + ")", Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); } } public void testProviderMethodWithSubclassOfExceptionIsOk() { providesInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(RemoteProvider.class) String foo() throws AccessException { throw new AccessException("boo!"); } }); RemoteProvider<String> remoteProvider = providesInjector.getInstance(Key.get(remoteProviderOfString)); try { remoteProvider.get(); fail(); } catch (RemoteException expected) { assertTrue(expected instanceof AccessException); assertEquals("boo!", expected.getMessage()); } } public void testProviderMethodWithSuperclassFails() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(RemoteProvider.class) String foo() throws IOException { return null; } }); fail(); } catch (CreationException ce) { assertEquals( IOException.class.getName() + " is not compatible with the exceptions ([" + RemoteException.class + "]) declared in the CheckedProvider interface (" + RemoteProvider.class.getName() + ")", Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); } } public void testProviderMethodWithRuntimeExceptionsIsOk() throws RemoteException { providesInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(RemoteProvider.class) String foo() throws RuntimeException { throw new RuntimeException("boo!"); } }); RemoteProvider<String> remoteProvider = providesInjector.getInstance(Key.get(remoteProviderOfString)); try { remoteProvider.get(); fail(); } catch (RuntimeException expected) { assertEquals("boo!", expected.getCause().getMessage()); } } public void testProviderMethodWithManyExceptions() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(RemoteProvider.class) String foo() throws InterruptedException, RuntimeException, RemoteException, AccessException, TooManyListenersException { return null; } }); fail(); } catch (CreationException ce) { // The only two that should fail are Interrupted & TooManyListeners.. the rest are OK. List<Message> errors = ImmutableList.copyOf(ce.getErrorMessages()); assertEquals( InterruptedException.class.getName() + " is not compatible with the exceptions ([" + RemoteException.class + "]) declared in the CheckedProvider interface (" + RemoteProvider.class.getName() + ")", errors.get(0).getMessage()); assertEquals( TooManyListenersException.class.getName() + " is not compatible with the exceptions ([" + RemoteException.class + "]) declared in the CheckedProvider interface (" + RemoteProvider.class.getName() + ")", errors.get(1).getMessage()); assertEquals(2, errors.size()); } } public void testMoreTypeParameters() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(TooManyTypeParameters.class) String foo() { return null; } }); fail(); } catch (CreationException ce) { assertEquals( TooManyTypeParameters.class.getName() + " has more than one generic type parameter: [T, P]", Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); } } public void testWrongThrowingProviderType() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(WrongThrowingProviderType.class) String foo() { return null; } }); fail(); } catch (CreationException ce) { assertEquals( WrongThrowingProviderType.class.getName() + " does not properly extend CheckedProvider, the first type parameter of CheckedProvider " + "(java.lang.String) is not a generic type", Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); } } public void testOneMethodThatIsntGet() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(OneNoneGetMethod.class) String foo() { return null; } }); fail(); } catch (CreationException ce) { assertEquals( OneNoneGetMethod.class.getName() + " may not declare any new methods, but declared " + Classes.toString(OneNoneGetMethod.class.getDeclaredMethods()[0]), Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); } } public void testManyMethods() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(ManyMethods.class) String foo() { return null; } }); fail(); } catch (CreationException ce) { assertEquals( ManyMethods.class.getName() + " may not declare any new methods, but declared " + Arrays.asList(ManyMethods.class.getDeclaredMethods()), Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); } } public void testIncorrectPredefinedType_Bind() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { ThrowingProviderBinder.create(binder()) .bind(StringRemoteProvider.class, Integer.class) .to( new StringRemoteProvider() { @Override public String get() throws RemoteException { return "A"; } }); } }); fail(); } catch (CreationException ce) { assertEquals( StringRemoteProvider.class.getName() + " expects the value type to be java.lang.String, but it was java.lang.Integer", Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); } } public void testIncorrectPredefinedType_Provides() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(StringRemoteProvider.class) Integer foo() { return null; } }); fail(); } catch (CreationException ce) { assertEquals( StringRemoteProvider.class.getName() + " expects the value type to be java.lang.String, but it was java.lang.Integer", Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); } } private static interface TooManyTypeParameters<T, P> extends ThrowingProvider<T, Exception> {} private static interface WrongThrowingProviderType<T> extends ThrowingProvider<String, Exception> {} private static interface OneNoneGetMethod<T> extends ThrowingProvider<T, Exception> { T bar(); } private static interface ManyMethods<T> extends ThrowingProvider<T, Exception> { T bar(); String baz(); } }