Repository: ArcBees/Jukito
Branch: master
Commit: 280d16cf220e
Files: 85
Total size: 266.5 KB
Directory structure:
gitextract_98m20j73/
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── codequality/
│ ├── checkstyle.xml
│ ├── opensource.java.header
│ └── suppressions.xml
├── jukito/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── jukito/
│ │ ├── All.java
│ │ ├── BindingsCollector.java
│ │ ├── Description.java
│ │ ├── EnvironmentDependentModules.java
│ │ ├── GuiceUtils.java
│ │ ├── InjectedAfterStatements.java
│ │ ├── InjectedBeforeStatements.java
│ │ ├── InjectedFrameworkMethod.java
│ │ ├── InjectedStatement.java
│ │ ├── JukitoInternal.java
│ │ ├── JukitoModule.java
│ │ ├── JukitoRunner.java
│ │ ├── MockProvider.java
│ │ ├── MockitoUsageValidator.java
│ │ ├── NamedUniqueAnnotations.java
│ │ ├── SpyImmutableInstanceProvider.java
│ │ ├── SpyProvider.java
│ │ ├── TestEagerSingleton.java
│ │ ├── TestMockSingleton.java
│ │ ├── TestModule.java
│ │ ├── TestScope.java
│ │ ├── TestSingleton.java
│ │ └── UseModules.java
│ └── test/
│ └── java/
│ └── org/
│ └── jukito/
│ ├── AllAnnotationTest.java
│ ├── AllNamedAnnotationTest.java
│ ├── AssistedInjectTest.java
│ ├── AutoBindMocksDisabledTest.java
│ ├── BindAnnotatedConcreteClassesTest.java
│ ├── BindSpyInstanceTest.java
│ ├── BindSpyTest.java
│ ├── BindingToProviderTest.java
│ ├── EDRunner.java
│ ├── EnvironmentDependentComponent.java
│ ├── ExternalSingleton.java
│ ├── ForceMockTest.java
│ ├── GeneralTest.java
│ ├── InnerClassTest.java
│ ├── InstallTest.java
│ ├── ModuleWithProvidesMethods.java
│ ├── NoModuleTest.java
│ ├── OldStyleAssistedInjectTest.java
│ ├── OneHundred.java
│ ├── ParentClassInnerClassModuleDiscoveryTest.java
│ ├── ParentTestClassBase.java
│ ├── ParentTestClassTest.java
│ ├── ProviderBindingTest.java
│ ├── ProviderTest.java
│ ├── ProvidesMethodTest.java
│ ├── ReportWriterTest.java
│ ├── RequestInjectionTest.java
│ ├── RequestStaticInjectionTest.java
│ ├── RespectProvidesAnnotationInModuleTest.java
│ ├── RespectTestScopeWhenUsingAbstractModuleTest.java
│ ├── SampleParentTestClassWithInnerTestModule.java
│ ├── SingletonTest.java
│ ├── SomeCoreComponent.java
│ ├── SomeTestClass.java
│ ├── TestTestDescriptions.java
│ ├── TransitiveDependencyTest.java
│ ├── UseModulesTest.java
│ ├── Value3.java
│ └── environmentdependent/
│ ├── DesktopModule.java
│ ├── EnvironmentDependentModulesTest.java
│ ├── MobileModule.java
│ └── TabletModule.java
├── jukito-samples/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── jukito/
│ │ └── samples/
│ │ ├── Car.java
│ │ ├── DieselEngine.java
│ │ ├── Engine.java
│ │ ├── FordMustang.java
│ │ ├── PetrolEngine.java
│ │ └── modules/
│ │ ├── DieselLineModule.java
│ │ └── PetrolLineModule.java
│ └── test/
│ └── java/
│ └── org/
│ └── jukito/
│ └── samples/
│ ├── FordMustangTest.java
│ ├── FordMustangTest2.java
│ └── FordMustangTest3.java
└── pom.xml
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
gwt-unitCache
www-test
target
.project
*.iml
.settings
.idea
.classpath
.gwt
bin
META-INF
.checkstyle
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
================================================
FILE: LICENSE.md
================================================
# License
Jukito is freely distributable under the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0.html).
================================================
FILE: README.md
================================================

### The combined power of JUnit, Guice and Mockito. Plus it sounds like a cool martial art.
-----
So you started using dependency injection because somebody told you it would make your tests simpler? But as you gaze at your deep hierarchy of test classes, "simple" is not exactly the word you think of. Plus, creating a new mock whenever you add a parameter to an injected constructor gets old very quickly.
You are not alone! And Jukito was created specifically for people like you. Read on, or [get started](https://github.com/ArcBees/Jukito/wiki) right away!
If you use [Google Guice](http://code.google.com/p/google-guice/), or if your GWT application uses [Gin](http://code.google.com/p/google-gin/), then Jukito is the perfect antidote to your unit testing headaches. Now you can write tests like this:
```java
@RunWith(JukitoRunner.class)
public class EmailSystemTest {
@Inject EmailSystemImpl emailSystem;
Email dummyEmail;
@Before
public void setupMocks(
IncomingEmails incomingEmails,
EmailFactory factory) {
dummyEmail = factory.createDummy();
when(incomingEmails.count()).thenReturn(1);
when(incomingEmails.get(0)).thenReturn(dummyEmail);
}
@Test
public void shouldFetchEmailWhenStarting(
EmailView emailView) {
// WHEN
emailSystem.start();
// THEN
verify(emailView).addEmail(dummyEmail);
}
}
```
That's right, Jukito lets you `@Inject` fields exactly as if your test class was injected with Guice. You can also inject parameters into your `@Test`, `@Before` and `@After` methods. Guice's just-in-time binding automatically instantiate your concrete classes, like `EmailFactory`. What about interfaces like `IncomingEmails` or `EmailView`? Jukito mocks them out automatically for you using [mockito](https://code.google.com/p/mockito/)!
Let's look at another example:
```java
@RunWith(JukitoRunner.class)
public class CalculatorTest {
public static class Module extends JukitoModule {
protected void configureTest() {
bindMany(Calculator.class,
ScientificCalculator.class,
BusinessCalculator.class);
bindManyInstances(AdditionExample.class,
new AdditionExample(1, 1, 2),
new AdditionExample(10, 10, 20),
new AdditionExample(18, 24, 42));
}
}
@Test
public void testAdd(@All Calculator calculator, @All AdditionExample example) {
// WHEN
int result = calculator.add(example.a, example.b);
// THEN
assertEquals(example.expected, result);
}
}
```
As you see here, Jukito lets you define your very own test module, where you can bind classes just like a regular Guice module. It doesn't stop there, however. The `bindMany` methods let you bind different classes or instances to the same interface. Combined with the powerful `@All` annotation this lets you easily run a single test on a whole suite of test examples. The code above will run a total of six tests!
## Getting Started
[Read the wiki](https://github.com/ArcBees/Jukito/wiki) to find out everything Jukito has to offer, and [join the discussion](http://groups.google.com/group/jukito)!
## Latest Release
* 1.5
## Links
* [Jukito Custom Google Search](http://www.google.com/cse/home?cx=011138278718949652927:turyqq9pl64) - Search GWTP documentation, wiki and thread collections.
* [Jukito WebSite Source](https://github.com/ArcBees/jukito-website) - Jukito website source.
* [Jukito Google Group](https://groups.google.com/forum/?fromgroups#!forum/jukito) - Find help here.
* [Jukito Previous Repo](https://code.google.com/p/jukito/) - Previous home of Jukito.
* [GWTP](https://github.com/ArcBees/GWTP) - Find out more about GWT-Platform.
## Thanks to
[](http://arcbees.com)
[](https://www.atlassian.com/)
[](http://www.jetbrains.com/idea/index.html)
================================================
FILE: codequality/checkstyle.xml
================================================
================================================
FILE: codequality/opensource.java.header
================================================
/\*\*?
\* Copyright \d{4} ArcBees 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\.
\*/
================================================
FILE: codequality/suppressions.xml
================================================
================================================
FILE: jukito/pom.xml
================================================
4.0.0
org.jukito
jukito-parent
1.6-SNAPSHOT
jukito
jar
jukito
org.apache.maven.plugins
maven-compiler-plugin
org.apache.maven.plugins
maven-surefire-plugin
org.apache.maven.plugins
maven-deploy-plugin
org.apache.maven.plugins
maven-checkstyle-plugin
org.apache.maven.plugins
maven-source-plugin
org.apache.maven.plugins
maven-release-plugin
org.codehaus.mojo
animal-sniffer-maven-plugin
org.apache.maven.plugins
maven-javadoc-plugin
com.github.github
site-maven-plugin
org.mockito
mockito-core
junit
junit
com.google.inject
guice
com.google.inject.extensions
guice-assistedinject
================================================
FILE: jukito/src/main/java/org/jukito/All.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.google.inject.BindingAnnotation;
/**
* This annotation can be used on one or more parameter of a test function.
* The test function will be executed multiple times, one for each value bound
* to the parameter type.
*
* If more than one parameter is annotated with {@literal @}{@link All} then
* all combinations will be used. Therefore, be careful when using it on more than
* two or three parameters as it can result in a combinatorial explosion.
*
* Using the additional parameter {@link #value()} a subset of all bound values
* can be specified to be run in the test function.
*
* @see {@link TestModule#bindMany}
* @see {@link TestModule#bindManyInstances}
* @see {@link TestModule#bindManyNamed}
* @see {@link TestModule#bindManyNamedInstances}
*/
@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface All {
String DEFAULT = "__ALL__";
/**
* Used in conjunction with {@link org.jukito.JukitoModule#bindManyNamed(Class, String, Class[])} and related
* methods to retrieve all objects binded with the given name.
*/
String value() default DEFAULT;
}
================================================
FILE: jukito/src/main/java/org/jukito/BindingsCollector.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.google.inject.AbstractModule;
import com.google.inject.Binding;
import com.google.inject.Key;
import com.google.inject.Scope;
import com.google.inject.spi.ConstructorBinding;
import com.google.inject.spi.ConvertedConstantBinding;
import com.google.inject.spi.DefaultBindingScopingVisitor;
import com.google.inject.spi.DefaultBindingTargetVisitor;
import com.google.inject.spi.DefaultElementVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.Elements;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.InstanceBinding;
import com.google.inject.spi.LinkedKeyBinding;
import com.google.inject.spi.Message;
import com.google.inject.spi.PrivateElements;
import com.google.inject.spi.ProviderBinding;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderKeyBinding;
import com.google.inject.spi.StaticInjectionRequest;
import com.google.inject.spi.UntargettedBinding;
/**
* Collects all the bindings from a Guice module, so that Jukito can identify missing
* bindings and bind them to mock or instances.
*/
public class BindingsCollector {
/**
* Information on a binding, used by Jukito to identify provided keys and needed keys.
*/
public static class BindingInfo {
public Object boundInstance;
Key> key;
Key> boundKey;
String scope;
public static BindingInfo create(Binding> binding, Key> boundKey,
Object instance) {
BindingInfo bindingInfo = new BindingInfo();
bindingInfo.key = binding.getKey();
bindingInfo.boundKey = boundKey;
bindingInfo.boundInstance = instance;
bindingInfo.scope = binding.acceptScopingVisitor(new GuiceScopingVisitor());
return bindingInfo;
}
public static BindingInfo create(Key> boundKey) {
BindingInfo bindingInfo = new BindingInfo();
bindingInfo.boundKey = boundKey;
return bindingInfo;
}
}
private final AbstractModule module;
private final List bindingsObserved = new ArrayList<>();
private final List messages = new ArrayList<>();
BindingsCollector(AbstractModule module) {
this.module = module;
}
public void collectBindings() {
GuiceElementVisitor visitor = new GuiceElementVisitor();
visitor.visitElements(Elements.getElements(module));
// TODO report errors?
}
public List getBindingsObserved() {
return bindingsObserved;
}
/**
* This visitor collects all information on various guice elements.
*/
public class GuiceElementVisitor extends DefaultElementVisitor {
private void visitElements(List elements) {
for (Element element : elements) {
element.acceptVisitor(this);
}
}
@Override
public Void visit(com.google.inject.Binding command) {
GuiceBindingVisitor bindingVisitor = new GuiceBindingVisitor<>();
command.acceptTargetVisitor(bindingVisitor);
return null;
}
@SuppressWarnings("unchecked")
@Override
public Void visit(PrivateElements privateElements) {
Set> exposedKeys = privateElements.getExposedKeys();
for (Element element : privateElements.getElements()) {
if (element instanceof Binding>) {
Binding> bindingElement = (Binding>) element;
if (exposedKeys.contains(bindingElement.getKey())) {
@SuppressWarnings("rawtypes")
GuicePrivateBindingVisitor bindingVisitor = new GuicePrivateBindingVisitor();
bindingElement.acceptTargetVisitor(bindingVisitor);
}
}
}
return null;
}
@Override
public Void visit(StaticInjectionRequest staticInjectionRequest) {
for (InjectionPoint injectionPoint : staticInjectionRequest.getInjectionPoints()) {
addInjectionPointDependencies(injectionPoint);
}
return super.visit(staticInjectionRequest);
}
@Override
public Void visit(Message message) {
messages.add(message);
return null;
}
private void addInjectionPointDependencies(InjectionPoint injectionPoint) {
// Do not consider dependencies coming from optional injections.
if (injectionPoint.isOptional()) {
return;
}
for (Dependency> dependency : injectionPoint.getDependencies()) {
Key> key = dependency.getKey();
bindingsObserved.add(BindingInfo.create(key));
}
}
}
/**
* This visitor collects all information on guice bindings.
*/
public class GuiceBindingVisitor extends DefaultBindingTargetVisitor {
protected Void addBindingInfo(Binding extends T> binding, Key> boundKey, Object instance) {
bindingsObserved.add(BindingInfo.create(binding, boundKey, instance));
return null;
}
private Void addBinding(Binding extends T> binding) {
return addBindingInfo(binding, binding.getKey(), null);
}
private Void addBindingKey(Binding extends T> binding, Key> boundKey) {
return addBindingInfo(binding, boundKey, null);
}
private Void addBindingInstance(Binding extends T> binding, Object instance) {
return addBindingInfo(binding, null, instance);
}
@Override
public Void visit(ProviderBinding extends T> providerBinding) {
return addBindingKey(providerBinding, providerBinding.getProvidedKey());
}
@Override
public Void visit(ProviderKeyBinding extends T> providerKeyBinding) {
return addBindingKey(providerKeyBinding, providerKeyBinding.getProviderKey());
}
@Override
public Void visit(ProviderInstanceBinding extends T> providerInstanceBinding) {
return addBindingInstance(providerInstanceBinding,
providerInstanceBinding.getProviderInstance());
}
@Override
public Void visit(InstanceBinding extends T> instanceBinding) {
return addBindingInstance(instanceBinding, instanceBinding.getInstance());
}
@Override
public Void visit(ConvertedConstantBinding extends T> constantBinding) {
return addBindingInstance(constantBinding, constantBinding.getValue());
}
@Override
public Void visit(UntargettedBinding extends T> untargettedBinding) {
return addBinding(untargettedBinding);
}
@Override
public Void visit(LinkedKeyBinding extends T> linkedKeyBinding) {
return addBindingKey(linkedKeyBinding, linkedKeyBinding.getLinkedKey());
}
@Override
public Void visit(ConstructorBinding extends T> constructorBinding) {
return addBinding(constructorBinding);
}
}
/**
* This visitor collects the bindings for PrivateModules. Because the child
* elements are private, the bound keys are not recorded.
*/
public class GuicePrivateBindingVisitor extends GuiceBindingVisitor {
@Override
public Void visit(LinkedKeyBinding extends T> linkedKeyBinding) {
return addBindingInfo(linkedKeyBinding, null, null);
}
}
/**
* This visitor collects all information on guice scopes associated to the bindings.
*/
public static class GuiceScopingVisitor extends DefaultBindingScopingVisitor {
@Override
public String visitEagerSingleton() {
return "EagerSingleton";
}
@Override
public String visitScope(Scope scope) {
return scope.toString();
}
@Override
public String visitScopeAnnotation(Class extends Annotation> scopeAnnotation) {
return scopeAnnotation.getCanonicalName();
}
}
}
================================================
FILE: jukito/src/main/java/org/jukito/Description.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation can be used for better test description (instead of camel case test method).
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Description {
String value();
}
================================================
FILE: jukito/src/main/java/org/jukito/EnvironmentDependentModules.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.google.inject.Module;
/**
* Annotation used for declaration of modules which are installed
* and used separately in different applications
* but test for it collaborators are the same and thus
* run for against Environment Dependent Modules.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnvironmentDependentModules {
Class extends Module>[] value();
}
================================================
FILE: jukito/src/main/java/org/jukito/GuiceUtils.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import com.google.inject.ConfigurationException;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.Annotations;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
/**
* A number of useful static methods to manipulate Guice object. Most are
* taken from the source code of Guice, but cannot be accessed in there.
*/
public class GuiceUtils {
public static boolean isProvider(Key> key) {
return key.getTypeLiteral().getRawType().equals(Provider.class);
}
@SuppressWarnings("unchecked")
public static TypeLiteral getProvidedType(
TypeLiteral extends Provider extends T>> initialProviderTypeLiteral,
Errors errors) throws ErrorsException {
TypeLiteral extends Provider extends T>> providerTypeLiteral = initialProviderTypeLiteral;
while (providerTypeLiteral.getRawType() != Provider.class) {
providerTypeLiteral = (TypeLiteral extends Provider extends T>>)
providerTypeLiteral.getSupertype(Provider.class);
}
Type providerType = providerTypeLiteral.getType();
// If the Provider has no type parameter (raw Provider)...
if (!(providerType instanceof ParameterizedType)) {
throw errors.cannotInjectRawProvider().toException();
}
Type entryType = ((ParameterizedType) providerType).getActualTypeArguments()[0];
return (TypeLiteral) TypeLiteral.get(entryType);
}
public static Key getProvidedKey(Key> key,
Errors errors) throws ErrorsException {
TypeLiteral providedType = getProvidedType(key.getTypeLiteral(), errors);
Key providedKey;
if (key.getAnnotation() == null) {
providedKey = (Key) Key.get(providedType);
} else {
providedKey = (Key) Key.get(providedType, key.getAnnotation());
}
return providedKey;
}
@SuppressWarnings("unchecked")
public static Key ensureProvidedKey(Key key, Errors errors) {
try {
return isProvider(key) ? getProvidedKey((Key>) key, errors) : key;
} catch (ConfigurationException e) {
errors.merge(e.getErrorMessages());
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
return null;
}
public static List> getMethodKeys(Method method, Errors errors) {
Annotation allParameterAnnotations[][] = method.getParameterAnnotations();
List> result = new ArrayList>(allParameterAnnotations.length);
Iterator annotationsIterator = Arrays.asList(allParameterAnnotations).iterator();
TypeLiteral> type = TypeLiteral.get(method.getDeclaringClass());
for (TypeLiteral> parameterType : type.getParameterTypes(method)) {
try {
Annotation[] parameterAnnotations = annotationsIterator.next();
result.add(Annotations.getKey(parameterType, method, parameterAnnotations, errors));
} catch (ConfigurationException e) {
errors.merge(e.getErrorMessages());
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
}
return result;
}
}
================================================
FILE: jukito/src/main/java/org/jukito/InjectedAfterStatements.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.util.ArrayList;
import java.util.List;
import org.junit.internal.runners.model.MultipleFailureException;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import com.google.inject.Injector;
/**
* A {@link Statement} invoking a list of methods with parameters by filling-in
* these parameters with injected instances. The methods are called after the
* provided {@code prev} {@link Statement}.
*/
public class InjectedAfterStatements extends Statement {
private final Statement prev;
private final List afters;
public InjectedAfterStatements(Statement prev, List afters,
Object target, Injector injector) {
this.prev = prev;
this.afters = new ArrayList(afters.size());
for (FrameworkMethod method : afters) {
this.afters.add(new InjectedStatement(method, target, injector));
}
}
@Override
public void evaluate() throws Throwable {
List errors = new ArrayList();
errors.clear();
try {
prev.evaluate();
} catch (Throwable e) {
errors.add(e);
} finally {
for (Statement after : afters) {
try {
after.evaluate();
} catch (Throwable e) {
errors.add(e);
}
}
}
if (!errors.isEmpty()) {
throw new MultipleFailureException(errors);
}
}
}
================================================
FILE: jukito/src/main/java/org/jukito/InjectedBeforeStatements.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.util.ArrayList;
import java.util.List;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import com.google.inject.Injector;
/**
* A {@link Statement} invoking a list of methods with parameters by filling-in
* these parameters with injected instances. The methods are called before the
* provided {@code next} {@link Statement}.
*/
public class InjectedBeforeStatements extends Statement {
private final Statement next;
private final List befores;
public InjectedBeforeStatements(Statement next, List befores,
Object target, Injector injector) {
this.next = next;
this.befores = new ArrayList(befores.size());
for (FrameworkMethod method : befores) {
this.befores.add(new InjectedStatement(method, target, injector));
}
}
@Override
public void evaluate() throws Throwable {
for (Statement before : befores) {
before.evaluate();
}
next.evaluate();
}
}
================================================
FILE: jukito/src/main/java/org/jukito/InjectedFrameworkMethod.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.reflect.Method;
import java.util.List;
import org.junit.runners.model.FrameworkMethod;
import com.google.inject.Binding;
/**
* This class is an extension of {@link FrameworkMethod} that makes it possible to specify
* which {@link Binding} to use for parameters marked with {@literal @}{@link All}.
*/
public class InjectedFrameworkMethod extends FrameworkMethod {
private final List> bindingsToUseForParameters;
public InjectedFrameworkMethod(Method method) {
super(method);
bindingsToUseForParameters = null;
}
public InjectedFrameworkMethod(Method method, List> bindingsToUseForParameters) {
super(method);
this.bindingsToUseForParameters = bindingsToUseForParameters;
}
public List> getBindingsToUseForParameters() {
return bindingsToUseForParameters;
}
}
================================================
FILE: jukito/src/main/java/org/jukito/InjectedStatement.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import com.google.inject.Binding;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.internal.Errors;
/**
* A {@link Statement} invoking a method with parameters by filling-in these
* parameters with injected instances.
*/
class InjectedStatement extends Statement {
private final FrameworkMethod method;
private final Object test;
private final Injector injector;
InjectedStatement(FrameworkMethod method, Object test, Injector injector) {
this.method = method;
this.test = test;
this.injector = injector;
}
@Override
public void evaluate() throws Throwable {
Method javaMethod = method.getMethod();
Injector methodInjector = injector;
UseModules useModules = javaMethod.getAnnotation(UseModules.class);
if (useModules != null) {
Class extends Module>[] moduleClasses = useModules.value();
final Module[] modules = new Module[moduleClasses.length];
for (int i = 0; i < modules.length; i++) {
modules[i] = moduleClasses[i].newInstance();
}
TestModule jukitoModule;
if (useModules.autoBindMocks()) {
jukitoModule = new JukitoModule() {
@Override
protected void configureTest() {
for (Module m : modules) {
install(m);
}
}
};
} else {
jukitoModule = new TestModule() {
@Override
protected void configureTest() {
for (Module m : modules) {
install(m);
}
}
};
}
methodInjector = Guice.createInjector(jukitoModule);
}
Errors errors = new Errors(javaMethod);
List> keys = GuiceUtils.getMethodKeys(javaMethod, errors);
errors.throwConfigurationExceptionIfErrorsExist();
Iterator> bindingIter;
if (InjectedFrameworkMethod.class.isAssignableFrom(method.getClass())) {
bindingIter = ((InjectedFrameworkMethod) method).getBindingsToUseForParameters().iterator();
} else {
bindingIter = new ArrayList>().iterator();
}
List injectedParameters = new ArrayList();
for (Key> key : keys) {
if (!All.class.equals(key.getAnnotationType())) {
injectedParameters.add(methodInjector.getInstance(key));
} else {
if (!bindingIter.hasNext()) {
throw new AssertionError("Expected more bindings to fill @All parameters.");
}
injectedParameters.add(methodInjector.getInstance(bindingIter.next().getKey()));
}
}
method.invokeExplosively(test, injectedParameters.toArray());
}
}
================================================
FILE: jukito/src/main/java/org/jukito/JukitoInternal.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.google.inject.BindingAnnotation;
/**
* An internal binding annotation used when binding {@link SpyProvider}.
* See {@link TestModule#bindSpy(Class)}.
*/
@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@interface JukitoInternal {
}
================================================
FILE: jukito/src/main/java/org/jukito/JukitoModule.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.jukito.BindingsCollector.BindingInfo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.google.inject.ConfigurationException;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ProviderMethod;
import com.google.inject.internal.ProviderMethodsModule;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.InjectionPoint;
/**
* A guice {@link com.google.inject.Module Module} with a bit of syntactic sugar
* to bind within typical test scopes. Depends on mockito. This module
* automatically mocks any interface or abstract class dependency for which a
* binding is not explicitly provided. Any concrete class for which a binding is
* not explicitly provided is bound as a {@link TestScope#SINGLETON}.
*
* Depends on Mockito.
*/
public abstract class JukitoModule extends TestModule {
protected List bindingsObserved = Collections.emptyList();
private final Set> forceMock = new HashSet<>();
private final Set> dontForceMock = new HashSet<>();
private final List> keysNeedingTransitiveDependencies = new ArrayList<>();
private final Map, Object> primitiveTypes = new HashMap<>();
public JukitoModule() {
primitiveTypes.put(String.class, "");
primitiveTypes.put(Integer.class, 0);
primitiveTypes.put(Long.class, 0L);
primitiveTypes.put(Boolean.class, false);
primitiveTypes.put(Double.class, 0.0);
primitiveTypes.put(Float.class, 0.0f);
primitiveTypes.put(Short.class, (short) 0);
primitiveTypes.put(Character.class, '\0');
primitiveTypes.put(Byte.class, (byte) 0);
primitiveTypes.put(Class.class, Object.class);
}
/**
* Attach this {@link JukitoModule} to a list of the bindings that were
* observed by a preliminary run of {@link BindingsCollector}.
*
* @param bindingsObserved The observed bindings.
*/
public void setBindingsObserved(List bindingsObserved) {
this.bindingsObserved = bindingsObserved;
}
/**
* By default, only abstract classes, interfaces and classes annotated with
* {@link TestMockSingleton} are automatically mocked. Use {@link #forceMock}
* to indicate that all concrete classes derived from the a specific type
* will be mocked in {@link TestMockSingleton} scope.
*
* @param klass The {@link Class} or interface for which all subclasses will be mocked.
*/
protected void forceMock(Class> klass) {
forceMock.add(klass);
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public final void configure() {
bindScopes();
configureTest();
Set> keysObserved = new HashSet<>(bindingsObserved.size());
Set> keysNeeded = new HashSet<>(bindingsObserved.size());
for (BindingInfo bindingInfo : bindingsObserved) {
if (bindingInfo.key != null) {
keysObserved.add(bindingInfo.key);
}
if (bindingInfo.boundKey != null) {
keysNeeded.add(bindingInfo.boundKey);
}
if (bindingInfo.boundInstance != null &&
bindingInfo.boundInstance instanceof HasDependencies) {
HasDependencies hasDependencies = (HasDependencies) bindingInfo.boundInstance;
for (Dependency> dependency : hasDependencies.getDependencies()) {
keysNeeded.add(dependency.getKey());
}
}
}
// registering keys build via @Provides methods in this module in the keysObserved set.
ProviderMethodsModule providerMethodsModule = (ProviderMethodsModule)
ProviderMethodsModule.forModule(this);
List> providerMethodList = providerMethodsModule.getProviderMethods(binder());
for (ProviderMethod> providerMethod : providerMethodList) {
keysObserved.add(providerMethod.getKey());
}
// Make sure needed keys from Guice bindings are bound as mock or to instances
// (but not as test singletons)
for (Key> keyNeeded : keysNeeded) {
addNeededKey(keysObserved, keysNeeded, keyNeeded, false);
keysNeedingTransitiveDependencies.add(keyNeeded);
}
// Preempt JIT binding by looking through the test class and any parent class
// looking for methods annotated with @Test, @Before, or @After.
// Concrete classes bound in this way are bound in @TestSingleton.
Class> currentClass = testClass;
while (currentClass != null) {
for (Method method : currentClass.getDeclaredMethods()) {
if (method.isAnnotationPresent(Test.class)
|| method.isAnnotationPresent(Before.class)
|| method.isAnnotationPresent(After.class)) {
Errors errors = new Errors(method);
List> keys = GuiceUtils.getMethodKeys(method, errors);
for (Key> key : keys) {
// Skip keys annotated with @All
if (!All.class.equals(key.getAnnotationType())) {
Key> keyNeeded = GuiceUtils.ensureProvidedKey(key, errors);
addNeededKey(keysObserved, keysNeeded, keyNeeded, true);
}
}
errors.throwConfigurationExceptionIfErrorsExist();
}
}
currentClass = currentClass.getSuperclass();
}
// Preempt JIT binding by looking through the test class looking for
// fields and methods annotated with @Inject.
// Concrete classes bound in this way are bound in @TestSingleton.
if (testClass != null) {
Set injectionPoints = InjectionPoint.forInstanceMethodsAndFields(testClass);
for (InjectionPoint injectionPoint : injectionPoints) {
Errors errors = new Errors(injectionPoint);
List> dependencies = injectionPoint.getDependencies();
for (Dependency> dependency : dependencies) {
Key> keyNeeded = GuiceUtils.ensureProvidedKey(dependency.getKey(),
errors);
addNeededKey(keysObserved, keysNeeded, keyNeeded, true);
}
errors.throwConfigurationExceptionIfErrorsExist();
}
}
// Recursively add the dependencies of all the bindings observed. Warning, we can't use for each here
// since it would result into concurrency issues.
for (int i = 0; i < keysNeedingTransitiveDependencies.size(); ++i) {
addDependencies(keysNeedingTransitiveDependencies.get(i), keysObserved, keysNeeded);
}
// Bind all keys needed but not observed as mocks.
for (Key> key : keysNeeded) {
Class> rawType = key.getTypeLiteral().getRawType();
if (!keysObserved.contains(key) && !isCoreGuiceType(rawType)
&& !isAssistedInjection(key)) {
Object primitiveInstance = getDummyInstanceOfPrimitiveType(rawType);
if (primitiveInstance == null) {
if (rawType != Provider.class && !isInnerClass(rawType)) {
bind(key).toProvider(new MockProvider(rawType)).in(TestScope.SINGLETON);
}
} else {
bindKeyToInstance(key, primitiveInstance);
}
}
}
}
private boolean isInnerClass(Class> rawType) {
return rawType.isMemberClass() && !Modifier.isStatic(rawType.getModifiers());
}
@SuppressWarnings("unchecked")
private void bindKeyToInstance(Key key, Object primitiveInstance) {
bind(key).toInstance((T) primitiveInstance);
}
private void addNeededKey(Set> keysObserved, Set> keysNeeded,
Key> keyNeeded, boolean asTestSingleton) {
keysNeeded.add(keyNeeded);
bindIfConcrete(keysObserved, keyNeeded, asTestSingleton);
}
private void bindIfConcrete(Set> keysObserved,
Key key, boolean asTestSingleton) {
TypeLiteral> typeToBind = key.getTypeLiteral();
Class> rawType = typeToBind.getRawType();
if (!keysObserved.contains(key) && canBeInjected(typeToBind)
&& !shouldForceMock(rawType) && !isAssistedInjection(key)) {
// If an @Singleton annotation is present, force the bind as TestSingleton
if (asTestSingleton ||
rawType.getAnnotation(Singleton.class) != null) {
bind(key).in(TestScope.SINGLETON);
} else {
bind(key);
}
keysObserved.add(key);
keysNeedingTransitiveDependencies.add(key);
}
}
private boolean canBeInjected(TypeLiteral> type) {
Class> rawType = type.getRawType();
if (isPrimitive(rawType) || isCoreGuiceType(rawType) || !isInstantiable(rawType)) {
return false;
}
try {
InjectionPoint.forConstructorOf(type);
return true;
} catch (ConfigurationException e) {
return false;
}
}
private boolean isAssistedInjection(Key> key) {
return key.getAnnotationType() != null
&& Assisted.class.isAssignableFrom(key.getAnnotationType());
}
private boolean shouldForceMock(Class> klass) {
if (dontForceMock.contains(klass)) {
return false;
}
if (forceMock.contains(klass)) {
return true;
}
// The forceMock set contains all the base classes the user wants
// to force mock, check id the specified klass is a subclass of one of
// these.
// Update forceMock or dontForceMock based on the result to speed-up
// future look-ups.
boolean result = false;
for (Class> classToMock : forceMock) {
if (classToMock.isAssignableFrom(klass)) {
result = true;
break;
}
}
if (result) {
forceMock.add(klass);
} else {
dontForceMock.add(klass);
}
return result;
}
private boolean isInstantiable(Class> klass) {
return !klass.isInterface() && !Modifier.isAbstract(klass.getModifiers());
}
private boolean isPrimitive(Class> klass) {
return getDummyInstanceOfPrimitiveType(klass) != null;
}
private Object getDummyInstanceOfPrimitiveType(Class> klass) {
Object instance = primitiveTypes.get(klass);
if (instance == null && Enum.class.isAssignableFrom(klass)) {
// Safe to ignore exception, Guice will fail with a reasonable error
// if the Enum is empty.
try {
instance = ((Object[]) klass.getMethod("values").invoke(null))[0];
} catch (Exception ignored) {
}
}
return instance;
}
private boolean isCoreGuiceType(Class> klass) {
return TypeLiteral.class.isAssignableFrom(klass)
|| Injector.class.isAssignableFrom(klass)
|| Logger.class.isAssignableFrom(klass)
|| Stage.class.isAssignableFrom(klass)
|| MembersInjector.class.isAssignableFrom(klass);
}
private void addDependencies(Key key, Set> keysObserved,
Set> keysNeeded) {
TypeLiteral type = key.getTypeLiteral();
if (!canBeInjected(type)) {
return;
}
addInjectionPointDependencies(InjectionPoint.forConstructorOf(type),
keysObserved, keysNeeded);
Set methodsAndFieldsInjectionPoints =
InjectionPoint.forInstanceMethodsAndFields(type);
for (InjectionPoint injectionPoint : methodsAndFieldsInjectionPoints) {
addInjectionPointDependencies(injectionPoint, keysObserved, keysNeeded);
}
}
private void addInjectionPointDependencies(InjectionPoint injectionPoint,
Set> keysObserved, Set> keysNeeded) {
// Do not consider dependencies coming from optional injections
if (injectionPoint.isOptional()) {
return;
}
for (Dependency> dependency : injectionPoint.getDependencies()) {
Key> key = dependency.getKey();
addKeyDependency(key, keysObserved, keysNeeded);
}
}
private void addKeyDependency(Key> key, Set> keysObserved,
Set> keysNeeded) {
Key> newKey = key;
if (Provider.class.equals(key.getTypeLiteral().getRawType())) {
Type providedType = (
(ParameterizedType) key.getTypeLiteral().getType()).getActualTypeArguments()[0];
if (key.getAnnotation() != null) {
newKey = Key.get(providedType, key.getAnnotation());
} else if (key.getAnnotationType() != null) {
newKey = Key.get(providedType, key.getAnnotationType());
} else {
newKey = Key.get(providedType);
}
}
addNeededKey(keysObserved, keysNeeded, newKey, true);
}
/**
* Override and return the {@link Writer} you want to use to report the tree of test objects,and whether they
* were mocked, spied, automatically instantiated, or explicitly bound. Mostly useful for
* debugging.
*
* @return The {@link Writer}, if {@code null} no report will be output.
*/
public Writer getReportWriter() {
return null;
}
/**
* Outputs the report, see {@link #setReportWriter(Writer)}. Will not output anything if the
* {@code reportWriter} is {@code null}. Do not call directly, it will be called by
* {@link JukitoRunner}. To obtain a report, override {@link #getReportWriter()}.
*/
public void printReport(List allBindings) {
Writer reportWriter = getReportWriter();
if (reportWriter == null) {
return;
}
try {
reportWriter.append("*** EXPLICIT BINDINGS ***\n");
Set> reportedKeys = outputBindings(reportWriter, bindingsObserved,
Collections.>emptySet());
reportWriter.append('\n');
reportWriter.append("*** AUTOMATIC BINDINGS ***\n");
outputBindings(reportWriter, allBindings, reportedKeys);
reportWriter.append('\n');
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @param reportWriter The {@link Writer} to use to output the report.
* @param bindings The bindings to report.
* @param keysToSkip The keys that should not be reported.
* @return All the keys that were reported.
* @throws IOException If something goes wrong when writing.
*/
private Set> outputBindings(Writer reportWriter, List bindings,
Set> keysToSkip) throws IOException {
Set> reportedKeys = new HashSet<>(bindings.size());
for (BindingInfo bindingInfo : bindings) {
if (keysToSkip.contains(bindingInfo.key)) {
continue;
}
reportedKeys.add(bindingInfo.key);
reportWriter.append(" ");
reportWriter.append(bindingInfo.key.toString());
reportWriter.append(" --> ");
if (bindingInfo.boundKey != null) {
if (bindingInfo.key == bindingInfo.boundKey) {
reportWriter.append("Bound directly");
} else {
reportWriter.append(bindingInfo.boundKey.toString());
}
} else if (bindingInfo.boundInstance != null) {
reportWriter.append("Instance of ").append(bindingInfo.boundInstance.getClass().getCanonicalName());
} else {
reportWriter.append("NOTHING!?");
}
reportWriter.append(" ### ");
if (bindingInfo.scope == null) {
reportWriter.append("No scope");
} else {
reportWriter.append("In scope ");
reportWriter.append(bindingInfo.scope);
}
reportWriter.append('\n');
}
return reportedKeys;
}
}
================================================
FILE: jukito/src/main/java/org/jukito/JukitoRunner.java
================================================
/*
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import com.google.inject.Binding;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Scope;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.Errors;
import com.google.inject.spi.DefaultBindingScopingVisitor;
/*
* This class implements the mockito runner but allows Guice dependency
* injection. To setup the guice environment, the test class can have an inner
* static class deriving from {@link TestModule}. This last class will let you bind
* {@link TestSingleton} and {@link TestEagerSingleton} and the runner will make sure these
* singletons are reset at every invocation of a test case.
*
*
* Most of the code here is inspired from: http://cowwoc.blogspot.com/2008/10/integrating-google-guice-into-junit4.
* html
*
* Depends on Mockito.
*/
public class JukitoRunner extends BlockJUnit4ClassRunner {
private Injector injector;
public JukitoRunner(Class> klass) throws InitializationError,
InvocationTargetException, InstantiationException, IllegalAccessException {
super(klass);
ensureInjector();
}
public JukitoRunner(Class> klass, Injector injector) throws InitializationError,
InvocationTargetException, InstantiationException, IllegalAccessException {
// refactor needed here cos ensureInjector is run without reason here.
super(klass);
this.injector = injector;
}
/**
* Creates an injector from a test module.
* Override this to use something like Netflix Governator.
*
* @param testModule the test module
* @return a newly created injector
*/
protected Injector createInjector(TestModule testModule) {
return Guice.createInjector(testModule);
}
private void ensureInjector()
throws InstantiationException, IllegalAccessException {
if (injector != null) {
return;
}
Class> testClass = getTestClass().getJavaClass();
TestModule testModule = getTestModule(testClass);
testModule.setTestClass(testClass);
JukitoModule jukitoModule = null; // Only non-null if it's a JukitoModule
if (testModule instanceof JukitoModule) {
jukitoModule = (JukitoModule) testModule;
// Create a module just for the purpose of collecting bindings
TestModule testModuleForCollection = getTestModule(testClass);
BindingsCollector collector = new BindingsCollector(testModuleForCollection);
collector.collectBindings();
jukitoModule.setBindingsObserved(collector.getBindingsObserved());
}
injector = this.createInjector(testModule);
if (jukitoModule != null && jukitoModule.getReportWriter() != null) {
// An output report is desired
BindingsCollector collector = new BindingsCollector(jukitoModule);
collector.collectBindings();
jukitoModule.printReport(collector.getBindingsObserved());
}
}
private TestModule getTestModule(Class> testClass) throws InstantiationException, IllegalAccessException {
Set> useModuleClasses = getUseModuleClasses(testClass);
boolean autoBindMocks = getAutoBindMocksValue(testClass);
if (!useModuleClasses.isEmpty()) {
return createJukitoModule(useModuleClasses, autoBindMocks);
}
TestModule testModule = getInnerClassModule(testClass);
if (testModule != null) {
return testModule;
}
if (autoBindMocks) {
return new JukitoModule() {
@Override
protected void configureTest() {
}
};
} else {
return new TestModule() {
@Override
protected void configureTest() {
}
};
}
}
/**
* Gets Guice modules registered with {@link UseModules} from test class and all super classes.
*
* @param testClass the test class running
* @return set of Guice modules
*/
private Set> getUseModuleClasses(Class> testClass) {
Class> currentClass = testClass;
Set> modules = new HashSet<>();
while (currentClass != null) {
UseModules useModules = currentClass.getAnnotation(UseModules.class);
if (useModules != null) {
Collections.addAll(modules, useModules.value());
}
currentClass = currentClass.getSuperclass();
}
return modules;
}
private TestModule getInnerClassModule(Class> testClass)
throws InstantiationException, IllegalAccessException {
Class> currentClass = testClass;
while (currentClass != null) {
for (Class> innerClass : currentClass.getDeclaredClasses()) {
if (TestModule.class.isAssignableFrom(innerClass)) {
return (TestModule) innerClass.newInstance();
}
}
currentClass = currentClass.getSuperclass();
}
return null;
}
private boolean getAutoBindMocksValue(Class> testClass) {
boolean autoBindMocks = true;
Class> currentClass = testClass;
while (currentClass != null) {
UseModules useModules = currentClass.getAnnotation(UseModules.class);
if (useModules != null) {
autoBindMocks = useModules.autoBindMocks();
break;
}
currentClass = currentClass.getSuperclass();
}
return autoBindMocks;
}
private TestModule createJukitoModule(final Iterable> moduleClasses,
boolean autoBindMocks) {
if (autoBindMocks) {
return new JukitoModule() {
@Override
protected void configureTest() {
for (Class extends Module> mClass : moduleClasses) {
try {
install(mClass.newInstance());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
};
} else {
return new TestModule() {
@Override
protected void configureTest() {
for (Class extends Module> mClass : moduleClasses) {
try {
install(mClass.newInstance());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
};
}
}
@Override
public void run(RunNotifier notifier) {
// add listener that validates framework usage at the end of each test
notifier.addListener(new MockitoUsageValidator(notifier));
super.run(notifier);
}
@Override
protected Object createTest() throws Exception {
TestScope.clear();
instantiateEagerTestSingletons();
return injector.getInstance(getTestClass().getJavaClass());
}
@Override
protected Statement methodInvoker(FrameworkMethod method, Object test) {
return new InjectedStatement(method, test, injector);
}
@Override
protected Statement withBefores(FrameworkMethod method, Object target,
Statement statement) {
try {
ensureInjector();
} catch (Exception e) {
throw new RuntimeException(e);
}
List befores = getTestClass().getAnnotatedMethods(
Before.class);
return befores.isEmpty() ? statement : new InjectedBeforeStatements(statement,
befores, target, injector);
}
@Override
protected Statement withAfters(FrameworkMethod method, Object target,
Statement statement) {
try {
ensureInjector();
} catch (Exception e) {
throw new RuntimeException(e);
}
List afters = getTestClass().getAnnotatedMethods(
After.class);
return afters.isEmpty() ? statement : new InjectedAfterStatements(statement,
afters, target, injector);
}
@Override
protected List computeTestMethods() {
try {
ensureInjector();
} catch (Exception e) {
throw new RuntimeException(e);
}
List testMethods = getTestClass().getAnnotatedMethods(Test.class);
List result = new ArrayList<>(testMethods.size());
for (FrameworkMethod method : testMethods) {
Method javaMethod = method.getMethod();
Errors errors = new Errors(javaMethod);
List> keys = GuiceUtils.getMethodKeys(javaMethod, errors);
errors.throwConfigurationExceptionIfErrorsExist();
List>> bindingsToUseForParameters = new ArrayList<>();
for (Key> key : keys) {
if (All.class.equals(key.getAnnotationType())) {
All allAnnotation = (All) key.getAnnotation();
TypeLiteral> typeLiteral = key.getTypeLiteral();
List> bindings = getBindingsForParameterWithAllAnnotation(allAnnotation, typeLiteral);
bindingsToUseForParameters.add(bindings);
}
}
// Add an injected method for every combination of binding
addAllBindingAssignations(bindingsToUseForParameters, 0,
new ArrayList>(bindingsToUseForParameters.size()),
javaMethod, result);
}
return result;
}
@Override
protected String testName(FrameworkMethod method) {
org.jukito.Description annotation = method.getMethod().getAnnotation(org.jukito.Description.class);
if (annotation != null) {
return annotation.value();
} else {
return super.testName(method);
}
}
/**
* Computes a list of all bindings which match a {@link All} annotation.
*
* @param allAnnotation the annotation to match
* @param typeLiteral the type of the bindings.
* @return the computed list.
*/
private List> getBindingsForParameterWithAllAnnotation(All allAnnotation, TypeLiteral> typeLiteral) {
List> result = new ArrayList<>();
String bindingName = allAnnotation.value();
if (All.DEFAULT.equals(bindingName)) {
// If the annotation is with the default name bind all bindings
result.addAll(injector.findBindingsByType(typeLiteral));
} else {
// Else bind only those bindings which have a key with the same name
for (Binding> binding : injector.findBindingsByType(typeLiteral)) {
if (NamedUniqueAnnotations.matches(bindingName, binding.getKey().getAnnotation())) {
result.add(binding);
}
}
}
return result;
}
/**
* This method looks at all possible way to assign the bindings in
* {@code bindingsToUseForParameters}, starting at index {@code index}.
* If {@code index} is larger than the number of elements in {@code bindingsToUseForParameters}
* then the {@code currentAssignation} with {@javaMethod} is added to {@code result}.
*
* @param result
* @param javaMethod
* @param bindingsToUseForParameters
* @param index
* @param currentAssignation
*/
private void addAllBindingAssignations(
List>> bindingsToUseForParameters, int index,
List> currentAssignation,
Method javaMethod, List result) {
if (index >= bindingsToUseForParameters.size()) {
List> assignation = new ArrayList<>(currentAssignation.size());
assignation.addAll(currentAssignation);
result.add(new InjectedFrameworkMethod(javaMethod, assignation));
return;
}
for (Binding> binding : bindingsToUseForParameters.get(index)) {
if (binding.getKey().getAnnotation() == null) {
// As TestModule.bindMany() annotates the bindings, the un-annotated bindings are typically unwanted
// mocks automatically bound by Jukito.
continue;
}
currentAssignation.add(binding);
if (currentAssignation.size() != index + 1) {
throw new AssertionError("Size of currentAssignation list is wrong.");
}
addAllBindingAssignations(bindingsToUseForParameters, index + 1,
currentAssignation,
javaMethod, result);
currentAssignation.remove(index);
}
}
private void instantiateEagerTestSingletons() {
DefaultBindingScopingVisitor isEagerTestScopeSingleton =
new DefaultBindingScopingVisitor() {
public Boolean visitScope(Scope scope) {
return scope == TestScope.EAGER_SINGLETON;
}
};
for (Binding> binding : injector.getBindings().values()) {
boolean instantiate = false;
if (binding != null) {
Boolean result = binding.acceptScopingVisitor(isEagerTestScopeSingleton);
if (result != null && result) {
instantiate = true;
}
}
if (instantiate) {
binding.getProvider().get();
}
}
}
/**
* Adds to {@code errors} for each method annotated with {@code @Test},
* {@code @Before}, or {@code @After} that is not a public, void instance
* method with no arguments.
*/
@Override
protected void validateInstanceMethods(List errors) {
validatePublicVoidMethods(After.class, false, errors);
validatePublicVoidMethods(Before.class, false, errors);
validateTestMethods(errors);
if (computeTestMethods().size() == 0) {
errors.add(new Exception("No runnable methods"));
}
}
/**
* Adds to {@code errors} for each method annotated with {@code @Test}that
* is not a public, void instance method with no arguments.
*/
@Override
protected void validateTestMethods(List errors) {
validatePublicVoidMethods(Test.class, false, errors);
}
/**
* Adds to {@code errors} if any method in this class is annotated with
* the provided {@code annotation}, but:
*
* is not public, or
* returns something other than void, or
* is static (given {@code isStatic is false}), or
* is not static (given {@code isStatic is true}).
*/
private void validatePublicVoidMethods(Class extends Annotation> annotation,
boolean isStatic, List errors) {
List methods = getTestClass().getAnnotatedMethods(annotation);
for (FrameworkMethod eachTestMethod : methods) {
eachTestMethod.validatePublicVoid(isStatic, errors);
}
}
/**
* Access the Guice injector.
*
* @return The Guice {@link Injector}.
*/
protected Injector getInjector() {
return injector;
}
}
================================================
FILE: jukito/src/main/java/org/jukito/MockProvider.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import com.google.inject.Provider;
import static org.mockito.Mockito.mock;
/**
* For use in test cases where an {@link Provider} is required to provide an
* object and the test case needs to provide a mock of the object.
*
* A new object is returned each the the provider is invoked, unless the object
* is bound as a {@link TestScope#SINGLETON} or {@link TestScope#EAGER_SINGLETON}.
*
* Depends on Mockito.
*
* @param The class to provide.
*/
public class MockProvider implements Provider {
private final Class classToProvide;
/**
* Construct a {@link Provider} that will return mocked objects of the specified types.
*
* @param classToProvide The {@link Class} of the mock object to provide.
*/
public MockProvider(Class classToProvide) {
this.classToProvide = classToProvide;
}
@Override
public T get() {
return mock(classToProvide);
}
}
================================================
FILE: jukito/src/main/java/org/jukito/MockitoUsageValidator.java
================================================
/*
* Copyright 2017 ArcBees 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 org.jukito;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;
import org.mockito.Mockito;
public class MockitoUsageValidator extends RunListener {
private final RunNotifier notifier;
public MockitoUsageValidator(RunNotifier notifier) {
this.notifier = notifier;
}
@Override
public void testFinished(org.junit.runner.Description description) throws Exception {
super.testFinished(description);
try {
Mockito.validateMockitoUsage();
} catch (Throwable t) {
notifier.fireTestFailure(new Failure(description, t));
}
}
}
================================================
FILE: jukito/src/main/java/org/jukito/NamedUniqueAnnotations.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.util.concurrent.atomic.AtomicInteger;
import com.google.inject.BindingAnnotation;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Factory for unique annotations with a name. Based on {@link com.google.inject.internal.UniqueAnnotations}.
*/
class NamedUniqueAnnotations {
private static final AtomicInteger nextUniqueValue = new AtomicInteger(1);
/**
* Returns if a NamedUniqueAnnotations matches a binding name.
*
* @param bindingName the name to match.
* @param annotation the annotation to match the name to.
* @return if the annotation matches the bindingName.
*/
public static boolean matches(String bindingName, java.lang.annotation.Annotation annotation) {
if (annotation instanceof Internal) {
return ((Internal) annotation).name().equals(bindingName);
}
return false;
}
/**
* Returns an annotation instance that is not equal to any other annotation
* instances, for use in creating distinct {@link com.google.inject.Key}s.
*
* @param name name to group multiple annotations. Each annotation is still unique even if it belongs to a group.
*/
public static Annotation create(String name) {
int unique = nextUniqueValue.getAndIncrement();
String nonNullName = name == null ? All.DEFAULT : name;
return new InternalImpl(unique, nonNullName);
}
@Retention(RUNTIME)
@BindingAnnotation
private @interface Internal {
String name();
int value();
}
private static class InternalImpl implements Internal {
private final int value;
private final String name;
InternalImpl(int value, String name) {
this.value = value;
this.name = name;
}
public int value() {
return value;
}
public String name() {
return name;
}
public Class extends Annotation> annotationType() {
return Internal.class;
}
@Override
public String toString() {
return "@" + Internal.class.getName() + "(name=" + name + ", value=" + value + ")";
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof Internal) {
Internal other = (Internal) o;
return value() == other.value() && name().equals(other.name());
}
return false;
}
@Override
public int hashCode() {
return (127 * name.hashCode()) ^ value;
}
}
}
================================================
FILE: jukito/src/main/java/org/jukito/SpyImmutableInstanceProvider.java
================================================
/**
* Copyright 2014 ArcBees 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 org.jukito;
import org.mockito.Mockito;
import com.google.inject.Provider;
/**
* For use in classes where you want to create a spied instance, as in
* {@link com.google.inject.binder.LinkedBindingBuilder#toInstance(T))},
* except the instance is a spy.
*
* A new spy is returned each time this {@link Provider} is invoked, wrapping the
* exact same instance class.
*
* Important: Spied object needs to be Immutable
*/
class SpyImmutableInstanceProvider implements Provider {
private final T instance;
/**
* Create a new {@link Provider} instance for use in creating spies of
* concrete instances.
*
* This instance should be immutable; if it is not, you risk polluting your tests as the underlying instance is the
* same (even though it uses a different spy wrapper).
*
* @param instance The instance to be returned.
*/
SpyImmutableInstanceProvider(T instance) {
this.instance = instance;
}
/**
* Create a new spy of your bound instance.
*/
@Override
public T get() {
return Mockito.spy(instance);
}
}
================================================
FILE: jukito/src/main/java/org/jukito/SpyProvider.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.util.Collections;
import java.util.Set;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import static org.mockito.Mockito.spy;
/**
* For use in test cases where an {@link Provider} is required to provide an
* object and the test case needs to provide a spy of the object.
*
* A new object is returned each the the provider is invoked, unless the object
* is bound as a {@link TestScope#SINGLETON} or {@link TestScope#EAGER_SINGLETON}.
*
* Depends on Mockito.
*
* @param The class to provide.
*/
class SpyProvider implements Provider, HasDependencies {
private final Provider rawProvider;
private final Set> dependencies;
/**
* Construct a {@link Provider} that will return spied instances of objects
* of the specified types. You should not call this directly, instead use
* {@link TestModule#bindSpy(Class)} or {@link TestModule#bindSpy(com.google.inject.TypeLiteral)}.
*
* @param rawProvider The test class, running with {@link JukitoRunner}.
* @param relayingKey The key of the binding used to relay to the real class. This should usually be the key of a
* {code toConstructor} binding. Internally, Jukito uses the {@link JukitoInternal} annotation to
* distinguish this binding.
*/
SpyProvider(Provider rawProvider, Key relayingKey) {
this.rawProvider = rawProvider;
dependencies = Collections.>singleton(Dependency.get(relayingKey));
}
@Override
public T get() {
return spy(rawProvider.get());
}
@Override
public Set> getDependencies() {
return dependencies;
}
}
================================================
FILE: jukito/src/main/java/org/jukito/TestEagerSingleton.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.google.inject.ScopeAnnotation;
/**
* This annotation can be used on any classes that should be bound
* within the {@link TestScope#EAGER_SINGLETON} scope. It is meant to be
* used on inner static classes of the test class or its parents
* and shouldn't be used on top-level classes.
*/
@ScopeAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface TestEagerSingleton {
}
================================================
FILE: jukito/src/main/java/org/jukito/TestMockSingleton.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation can be used on any classes that should be bound as
* a mock within the {@link TestScope#SINGLETON} scope. It is meant to be
* used on inner static classes of the test class or its parents
* and shouldn't be used on top-level classes.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TestMockSingleton {
}
================================================
FILE: jukito/src/main/java/org/jukito/TestModule.java
================================================
/**
* Copyright 2014 ArcBees 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 org.jukito;
import java.lang.reflect.Constructor;
import com.google.inject.AbstractModule;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.binder.ScopedBindingBuilder;
import com.google.inject.name.Names;
import com.google.inject.spi.InjectionPoint;
/**
* A guice {@link com.google.inject.Module Module} with a bit of syntactic sugar to bind within
* typical test scopes. Depends on mockito.
*
* Depends on Mockito.
*/
public abstract class TestModule extends AbstractModule {
protected Class> testClass;
/**
* Attach the {@link TestModule} to a given test class.
*
* @param testClass The test class to attach to this {@link TestModule}.
*/
public void setTestClass(Class> testClass) {
this.testClass = testClass;
}
@Override
public void configure() {
bindScopes();
configureTest();
}
protected void bindScopes() {
bindScope(TestSingleton.class, TestScope.SINGLETON);
bindScope(TestEagerSingleton.class, TestScope.EAGER_SINGLETON);
}
/**
* Configures a test {@link com.google.inject.Module Module} via the exposed methods.
*/
protected abstract void configureTest();
/**
* Binds an interface to a mocked version of itself. You will usually want to bind this in the
* {@link TestSingleton} scope.
*
* @param The type of the interface to bind
* @param klass The class to bind
* @return A {@link ScopedBindingBuilder}.
*/
protected ScopedBindingBuilder bindMock(Class klass) {
return bindNewMockProvider(Key.get(klass));
}
/**
* Binds an interface annotated with a {@link com.google.inject.name.Named @Named} to a
* mocked version of itself. You will usually want to bind this in the
* {@link TestSingleton} scope.
*
* @param The type of the interface to bind, a parameterized type
* @param typeLiteral The {@link TypeLiteral} corresponding to the parameterized type to bind.
* @return A {@link ScopedBindingBuilder}.
*/
protected ScopedBindingBuilder bindMock(
TypeLiteral typeLiteral) {
return bindNewMockProvider(Key.get(typeLiteral));
}
/**
* Binds a concrete object type so that spies of instances are returned
* instead of instances themselves. You will usually want to bind this in the
* {@link TestSingleton} scope.
*
* @param The type of the interface to bind
* @param klass The class to bind
* @return A {@link ScopedBindingBuilder}.
*/
protected ScopedBindingBuilder bindSpy(Class klass) {
return bindNewSpyProvider(Key.get(klass));
}
/**
* Binds a concrete object type so that spies of instances are returned
* instead of instances themselves. You will usually want to bind this in the
* {@link TestSingleton} scope.
*
* @param The type of the interface to bind, a parameterized type
* @param typeLiteral The {@link TypeLiteral} corresponding to the parameterized type to bind.
* @return A {@link ScopedBindingBuilder}.
*/
protected ScopedBindingBuilder bindSpy(
TypeLiteral typeLiteral) {
return bindNewSpyProvider(Key.get(typeLiteral));
}
/**
* Binds a concrete instance so that spies of this instance are returned
* instead of the object itself. Each spy is an independent spy but the
* underlying instance will be the same, so if the object is mutable,
* your tests can be polluted!
*
* You will usually want to bind this in the {@link TestSingleton} scope.
*
* @param The type of the interface to bind
* @param klass The class to bind
* @param instance The instance to bind this class to.
* @return A {@link ScopedBindingBuilder}.
*/
protected ScopedBindingBuilder bindSpy(Class klass, T instance) {
return bindNewSpyImmutableInstanceProvider(Key.get(klass), instance);
}
/**
* Binds a concrete instance so that spies of this instance are returned
* instead of the object itself. Each spy is an independent spy but the
* underlying instance will be the same, so if the object is mutable,
* your tests can be polluted!
*
* You will usually want to bind this in the
* {@link TestSingleton} scope.
*
* @param The type of the interface to bind, a parameterized type
* @param typeLiteral The {@link TypeLiteral} corresponding to the parameterized type to bind.
* @param instance The instance to bind this class to.
* @return A {@link ScopedBindingBuilder}.
*/
protected ScopedBindingBuilder bindSpy(
TypeLiteral typeLiteral, T instance) {
return bindNewSpyImmutableInstanceProvider(Key.get(typeLiteral), instance);
}
/**
* Binds an interface annotated with a {@link com.google.inject.name.Named @Named} to a
* mocked version of itself. You will usually want to bind this in the
* {@link TestSingleton} scope.
*
* @param The type of the interface to bind
* @param klass The class to bind
* @param name The name used with the {@link com.google.inject.name.Named @Named} annotation.
* @return A {@link ScopedBindingBuilder}.
*/
protected ScopedBindingBuilder bindNamedMock(Class klass, String name) {
return bindNewMockProvider(Key.get(klass, Names.named(name)));
}
/**
* Binds an interface annotated with a {@link com.google.inject.name.Named @Named} to a
* mocked version of itself. You will usually want to bind this in the
* {@link TestSingleton} scope.
*
* @param The type of the interface to bind
* @param typeLiteral The {@link TypeLiteral} corresponding to the parameterized type to bind.
* @param name The name used with the {@link com.google.inject.name.Named @Named} annotation.
* @return A {@link ScopedBindingBuilder}.
*/
protected ScopedBindingBuilder bindNamedMock(TypeLiteral typeLiteral,
String name) {
return bindNewMockProvider(Key.get(typeLiteral, Names.named(name)));
}
/**
* Binds a concrete object type annotated with a
* {@link com.google.inject.name.Named @Named} so that spies of instances are returned
* instead of instances themselves. You will usually want to bind this in the
* {@link TestSingleton} scope.
*
* @param The type of the interface to bind
* @param klass The class to bind
* @param name The name used with the {@link com.google.inject.name.Named @Named} annotation.
* @return A {@link ScopedBindingBuilder}.
*/
protected ScopedBindingBuilder bindNamedSpy(Class klass, String name) {
return bindNewSpyProvider(Key.get(klass, Names.named(name)));
}
/**
* Binds a concrete object type annotated with a
* {@link com.google.inject.name.Named @Named} so that spies of instances are returned
* instead of instances themselves. You will usually want to bind this in the
* {@link TestSingleton} scope.
*
* @param The type of the interface to bind
* @param typeLiteral The {@link TypeLiteral} corresponding to the parameterized type to bind.
* @param name The name used with the {@link com.google.inject.name.Named @Named} annotation.
* @return A {@link ScopedBindingBuilder}.
*/
protected ScopedBindingBuilder bindNamedSpy(TypeLiteral typeLiteral,
String name) {
return bindNewSpyProvider(Key.get(typeLiteral, Names.named(name)));
}
/**
* Binds a concrete instance annotated with {@link com.google.inject.name.Named @Named} so that spies of this
* instance are returned instead of the object itself. Each spy is an independent spy but the underlying instance
* will be the same, so if the object is mutable, your tests can be polluted!
*
* You will usually want to bind this in the {@link TestSingleton} scope.
*
* @param The type of the interface to bind
* @param klass The class to bind
* @param instance The instance to bind this class to.
* @param name The name used with the {@link com.google.inject.name.Named @Named} annotation.
* @return A {@link ScopedBindingBuilder}.
*/
protected ScopedBindingBuilder bindNamedSpy(Class klass, T instance,
String name) {
return bindNewSpyImmutableInstanceProvider(Key.get(klass, Names.named(name)), instance);
}
/**
* Binds a concrete instance annotated with {@link com.google.inject.name.Named @Named} so that spies of this
* instance are returned instead of the object itself. Each spy is an independent spy but the underlying instance
* will be the same, so if the object is mutable, your tests can be polluted!
*
* You will usually want to bind this in the {@link TestSingleton} scope.
*
* @param The type of the interface to bind
* @param typeLiteral The {@link TypeLiteral} corresponding to the parameterized type to bind.
* @param instance The instance to bind this class to.
* @param name The name used with the {@link com.google.inject.name.Named @Named} annotation.
* @return A {@link ScopedBindingBuilder}.
*/
protected ScopedBindingBuilder bindNamedSpy(TypeLiteral typeLiteral,
T instance, String name) {
return bindNewSpyImmutableInstanceProvider(Key.get(typeLiteral, Names.named(name)), instance);
}
@SuppressWarnings("unchecked")
private ScopedBindingBuilder bindNewMockProvider(Key key) {
return bind(key).toProvider(
new MockProvider((Class) key.getTypeLiteral().getRawType()));
}
@SuppressWarnings("unchecked")
private ScopedBindingBuilder bindNewSpyProvider(Key key) {
TypeLiteral type = key.getTypeLiteral();
InjectionPoint constructorInjectionPoint = InjectionPoint.forConstructorOf(type);
Key relayingKey = Key.get(type, JukitoInternal.class);
bind(relayingKey).toConstructor((Constructor) constructorInjectionPoint.getMember());
return bind(key).toProvider(new SpyProvider(getProvider(relayingKey), relayingKey));
}
private ScopedBindingBuilder bindNewSpyImmutableInstanceProvider(Key key, T instance) {
return bind(key).toProvider(new SpyImmutableInstanceProvider(instance));
}
/**
* This method binds many different instances to the same class or interface. Use this only
* if the instances are totally stateless. That is, they are immutable and have
* no mutable dependencies (e.g. a {@link String} or a simple POJO). For more
* complex classes use {@link #bindMany}.
*
* The specified {@link Class} will be bound to all the different instances, each
* binding using a different unique annotation.
*
* This method is useful when combined with the {@literal @}{@link All} annotation.
*
* @param clazz The {@link Class} to which the instances will be bound.
* @param instances All the instances to bind.
* @see {@link All}
*/
protected void bindManyInstances(Class clazz, V... instances) {
bindManyNamedInstances(clazz, All.DEFAULT, instances);
}
/**
* This method binds many different instances to the same class or interface. Use this only
* if the instances are totally stateless. That is, they are immutable and have
* no mutable dependencies (e.g. a {@link String} or a simple POJO). For more
* complex classes use {@link #bindMany}.
*
* The specified {@link Class} will be bound to all the different instances, each
* binding using a different unique but named annotation.
*
* This method is useful when combined with the {@literal @}{@link All} annotation with
* a name parameter.
*
* @param clazz The {@link Class} to which the instances will be bound.
* @param name The name to which to bind the instances.
* @param instances All the instances to bind.
* @see {@link All}
*/
protected void bindManyNamedInstances(Class clazz, String name, V... instances) {
for (V instance : instances) {
bind(clazz).annotatedWith(NamedUniqueAnnotations.create(name)).toInstance(instance);
}
}
/**
* This method binds many different instances to the same type literal. Use this only
* if the instances are totally stateless. That is, they are immutable and have
* no mutable dependencies (e.g. a {@link String} or a simple POJO). For more
* complex classes use {@link #bindMany}.
*
* The specified {@link TypeLiteral} will be bound to all the different instances, each
* binding using a different unique annotation.
*
* This method is useful when combined with the {@literal @}{@link All} annotation.
*
* @param type The {@link TypeLiteral} to which the instances will be bound.
* @param instances All the instances to bind.
* @see {@link All}
*/
protected void bindManyInstances(TypeLiteral type, V... instances) {
bindManyNamedInstances(type, All.DEFAULT, instances);
}
/**
* This method binds many different instances to the same class or interface. Use this only
* if the instances are totally stateless. That is, they are immutable and have
* no mutable dependencies (e.g. a {@link String} or a simple POJO). For more
* complex classes use {@link #bindMany}.
*
* The specified {@link Class} will be bound to all the different instances, each
* binding using a different unique but named annotation.
*
* This method is useful when combined with the {@literal @}{@link All} annotation with
* a name.
*
* @param type The {@link Class} to which the instances will be bound.
* @param name The name to which to bind the instances.
* @param instances All the instances to bind.
* @see {@link All}
*/
protected void bindManyNamedInstances(TypeLiteral type, String name, V... instances) {
for (V instance : instances) {
bind(type).annotatedWith(NamedUniqueAnnotations.create(name)).toInstance(instance);
}
}
/**
* This method binds many different classes to the same interface. All the
* classes will be bound within the {@link TestScope#SINGLETON} scope.
*
* This method is useful when combined with the {@literal @}{@link All} annotation.
*
* @param clazz The {@link Class} to which the instances will be bound.
* @param boundClasses All the classes to bind.
* @see {@link All}
*/
protected void bindMany(Class clazz, Class extends T>... boundClasses) {
bindManyNamed(clazz, All.DEFAULT, boundClasses);
}
/**
* This method binds many different type literals to the same type literal. All the
* classes will be bound within the {@link TestScope#SINGLETON} scope.
*
* This method is useful when combined with the {@literal @}{@link All} annotation with
* a name.
*
* @param clazz The {@link Class} to which the instances will be bound.
* @param name The name to which to bind the instances.
* @param boundClasses All the types to bind.
* @see {@link All}
*/
protected void bindManyNamed(Class clazz, String name, Class extends T>... boundClasses) {
for (Class extends T> boundClass : boundClasses) {
bind(clazz).annotatedWith(NamedUniqueAnnotations.create(name)).to(boundClass).in(TestScope.SINGLETON);
}
}
/**
* This method binds many different type literals to the same type literal. All the
* classes will be bound within the {@link TestScope#SINGLETON} scope.
*
* This method is useful when combined with the {@literal @}{@link All} annotation.
*
* @param type The {@link Class} to which the instances will be bound.
* @param boundTypes All the types to bind.
* @see {@link All}
*/
protected void bindMany(TypeLiteral type, TypeLiteral extends T>... boundTypes) {
bindManyNamed(type, All.DEFAULT, boundTypes);
}
/**
* This method binds many different type literals to the same type literal. All the
* classes will be bound within the {@link TestScope#SINGLETON} scope.
*
* This method is useful when combined with the {@literal @}{@link All} annotation with
* a name.
*
* @param type The {@link Class} to which the instances will be bound.
* @param name The name to which to bind the instances.
* @param boundTypes All the types to bind.
* @see {@link All}
*/
protected void bindManyNamed(TypeLiteral type, String name,
TypeLiteral extends T>... boundTypes) {
for (TypeLiteral extends T> boundType : boundTypes) {
bind(type).annotatedWith(NamedUniqueAnnotations.create(name)).to(boundType).in(TestScope.SINGLETON);
}
}
/**
* Binds an interface annotated with a {@link com.google.inject.name.Named @Named}.
*
* @param The type of the interface to bind
* @param klass The class to bind
* @param name The name used with the {@link com.google.inject.name.Named @Named} annotation.
* @return A {@link ScopedBindingBuilder}.
*/
protected LinkedBindingBuilder bindNamed(Class klass, String name) {
return bind(klass).annotatedWith(Names.named(name));
}
/**
* Binds an interface annotated with a {@link com.google.inject.name.Named @Named}.
*
* @param The type of the interface to bind
* @param typeLiteral The {@link TypeLiteral} corresponding to the parameterized type to bind.
* @param name The name used with the {@link com.google.inject.name.Named @Named} annotation.
* @return A {@link ScopedBindingBuilder}.
*/
protected LinkedBindingBuilder bindNamed(TypeLiteral typeLiteral,
String name) {
return bind(typeLiteral).annotatedWith(Names.named(name));
}
}
================================================
FILE: jukito/src/main/java/org/jukito/TestScope.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.util.HashMap;
import java.util.Map;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scope;
/**
* Container of the {@link #SINGLETON} and {@link #EAGER_SINGLETON} scopes for
* test cases running with the {@link JukitoRunner}. Depends on mockito.
*
* Depends on Mockito.
*/
public class TestScope {
private static class Singleton implements Scope {
private final String simpleName;
private final Map, Object> backingMap = new HashMap, Object>();
private Singleton(String simpleName) {
this.simpleName = simpleName;
}
public void clear() {
backingMap.clear();
}
@Override
public Provider scope(final Key key, final Provider unscoped) {
return new Provider() {
@SuppressWarnings("unchecked")
public T get() {
Object o = backingMap.get(key);
if (o == null) {
o = unscoped.get();
backingMap.put(key, o);
}
return (T) o;
}
};
}
public String toString() {
return simpleName;
}
}
/**
* Test-scoped singletons are typically used in test cases for objects that
* correspond to singletons in the application. Your JUnit test case must use
* {@link JukitoRunner} on its {@code @RunWith} annotation so that
* test-scoped singletons are reset before every test case.
*
* If you want your singleton to be instantiated automatically with each new
* test, use {@link #EAGER_SINGLETON}.
*/
public static final Singleton SINGLETON = new Singleton("TestSingleton");
/**
* Eager test-scoped singleton are similar to test-scoped {@link #SINGLETON}
* but they get instantiated automatically with each new test.
*/
public static final Singleton EAGER_SINGLETON = new Singleton("EagerTestSingleton");
/**
* Clears all the instances of test-scoped singletons. After this method is
* called, any "singleton" bound to this scope that had already been created
* will be created again next time it gets injected.
*/
public static void clear() {
SINGLETON.clear();
EAGER_SINGLETON.clear();
}
}
================================================
FILE: jukito/src/main/java/org/jukito/TestSingleton.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.google.inject.ScopeAnnotation;
/**
* This annotation can be used on any classes that should be bound
* within the {@link TestScope#SINGLETON} scope. It is meant to be
* used on inner static classes ofx the test class or its parents
* and shouldn't be used on top-level classes.
*/
@ScopeAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface TestSingleton {
}
================================================
FILE: jukito/src/main/java/org/jukito/UseModules.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.google.inject.Module;
/**
* This annotation can be used on a test class together with
* {@code @RunWith(JukitoRunner.class)} to use the bindings contained
* in the specified modules for the test.
*
* Example:
*
* {@literal @}RunWith(JukitoRunner.class)
* {@literal @}UseModules({ FooModule.class, BarModule.class}
* public class MyTest {
* {@literal @}Test
* public void someTest(TypeBoundInFooModule a, TypeBoundInBarModule b) {
* }
* }
*
* The example is equivalent to the following inner static module
* approach.
*
* {@literal @}RunWith(JukitoRunner.class)
* public class MyTest {
* static class Module extends JukitoModule {
* {@literal @}Override
* protected void configureTest() {
* install(new FooModule());
* install(new BarModule());
* }
* }
* // Test methods
* }
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface UseModules {
Class extends Module>[] value();
boolean autoBindMocks() default true;
}
================================================
FILE: jukito/src/test/java/org/jukito/AllAnnotationTest.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.util.ArrayList;
import java.util.List;
import org.junit.AfterClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Test that methods with some parameters annotated with {@literal @}{@link org.jukito.All} behave correctly.
*/
@RunWith(JukitoRunner.class)
public class AllAnnotationTest {
/**
* Guice test module.
*/
static class Module extends JukitoModule {
@SuppressWarnings("unchecked")
@Override
protected void configureTest() {
bindManyInstances(String.class, "A", "B");
bindManyInstances(TestDataInstance.class, new TestDataInstance("A"),
new TestDataInstance("B"));
bindMany(TestData.class, TestDataA.class, TestDataB.class);
bindMany(Node.class, NodeA.class);
}
}
interface TestData {
String getData();
}
static class TestDataA implements TestData {
public String getData() {
return "A";
}
}
static class TestDataB implements TestData {
public String getData() {
return "B";
}
}
static class TestDataInstance {
private final String data;
TestDataInstance(String data) {
this.data = data;
}
public String getData() {
return data;
}
}
interface Node {
}
static class NodeA implements Node {
}
/**
* This class keeps track of what happens in all the tests run in this
* class. It's used to make sure all expected tests are called.
*/
private static class Bookkeeper {
static List stringsProcessed = new ArrayList();
static List dataProcessed = new ArrayList();
static List dataInstanceProcessed = new ArrayList();
}
@Test
public void testAllWithInstance(@All String string1, @All String string2) {
Bookkeeper.stringsProcessed.add(string1 + string2);
}
@Test
public void testAllWithClass(@All TestData data1, @All TestData data2) {
Bookkeeper.dataProcessed.add(data1.getData() + data2.getData());
}
@Test
public void testAllWithClassInstance(@All TestDataInstance data1, @All TestDataInstance data2) {
Bookkeeper.dataInstanceProcessed.add(data1.getData() + data2.getData());
}
@AfterClass
public static void checkBookkeeper() {
assertTrue(Bookkeeper.stringsProcessed.contains("AA"));
assertTrue(Bookkeeper.stringsProcessed.contains("AB"));
assertTrue(Bookkeeper.stringsProcessed.contains("BA"));
assertTrue(Bookkeeper.stringsProcessed.contains("BB"));
assertEquals(4, Bookkeeper.stringsProcessed.size());
assertTrue(Bookkeeper.dataProcessed.contains("AA"));
assertTrue(Bookkeeper.dataProcessed.contains("AB"));
assertTrue(Bookkeeper.dataProcessed.contains("BA"));
assertTrue(Bookkeeper.dataProcessed.contains("BB"));
assertEquals(4, Bookkeeper.dataProcessed.size());
assertTrue(Bookkeeper.dataInstanceProcessed.contains("AA"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("AB"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("BA"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("BB"));
assertEquals(4, Bookkeeper.dataInstanceProcessed.size());
}
@Test
public void testAllDoesNotIncludeMock(@All Node node, Node neighbour) {
assertFalse(Mockito.mockingDetails(node).isMock());
}
}
================================================
FILE: jukito/src/test/java/org/jukito/AllNamedAnnotationTest.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.util.ArrayList;
import java.util.List;
import org.junit.AfterClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Test that methods with some parameters annotated with {@literal @}{@link All} behave correctly.
*/
@RunWith(JukitoRunner.class)
public class AllNamedAnnotationTest {
public static final String FIRST = "first";
public static final String SECOND = "second";
static class Module extends JukitoModule {
@SuppressWarnings("unchecked")
@Override
protected void configureTest() {
bindManyNamedInstances(String.class, FIRST, "A", "B");
bindManyNamedInstances(String.class, SECOND, "C", "D");
bindManyNamedInstances(TestDataInstance.class, FIRST, new TestDataInstance("A"),
new TestDataInstance("B"));
bindManyNamedInstances(TestDataInstance.class, SECOND, new TestDataInstance("C"),
new TestDataInstance("D"));
bindManyNamed(TestData.class, FIRST, TestDataA.class, TestDataB.class);
bindManyNamed(TestData.class, SECOND, TestDataC.class, TestDataD.class);
bindManyNamedInstances(Integer.class, null, 1, 2, 3, 5);
}
}
interface TestData {
String getData();
}
static class TestDataA implements TestData {
public String getData() {
return "A";
}
}
static class TestDataB implements TestData {
public String getData() {
return "B";
}
}
static class TestDataC implements TestData {
public String getData() {
return "C";
}
}
static class TestDataD implements TestData {
public String getData() {
return "D";
}
}
static class TestDataInstance {
private final String data;
TestDataInstance(String data) {
this.data = data;
}
public String getData() {
return data;
}
}
/**
* This class keeps track of what happens in all the tests run in this
* class. It's used to make sure all expected tests are called.
*/
private static class Bookkeeper {
static List namedStringsProcessed = new ArrayList();
static List namedDataProcessed = new ArrayList();
static List namedDataInstanceProcessed = new ArrayList();
static List stringsProcessed = new ArrayList();
static List dataProcessed = new ArrayList();
static List dataInstanceProcessed = new ArrayList();
static List integerProcessed = new ArrayList();
}
@Test
public void testAllWithNamedInstance(@All(FIRST) String string1, @All(SECOND) String string2) {
Bookkeeper.namedStringsProcessed.add(string1 + string2);
}
@Test
public void testAllWithNamedClass(@All(FIRST) TestData data1, @All(SECOND) TestData data2) {
Bookkeeper.namedDataProcessed.add(data1.getData() + data2.getData());
}
@Test
public void testAllWithNamedClassInstance(
@All(FIRST) TestDataInstance data1, @All(SECOND) TestDataInstance data2) {
Bookkeeper.namedDataInstanceProcessed.add(data1.getData() + data2.getData());
}
@Test
public void testAllWithInstance(@All String string1, @All String string2) {
Bookkeeper.stringsProcessed.add(string1 + string2);
}
@Test
public void testAllWithClass(@All TestData data1, @All TestData data2) {
Bookkeeper.dataProcessed.add(data1.getData() + data2.getData());
}
@Test
public void testAllWithClassInstance(@All TestDataInstance data1, @All TestDataInstance data2) {
Bookkeeper.dataInstanceProcessed.add(data1.getData() + data2.getData());
}
@Test
public void testAllWithNullAsName(@All Integer i) {
Bookkeeper.integerProcessed.add(i);
}
@AfterClass
public static void checkBookkeeper() {
assertTrue(Bookkeeper.namedStringsProcessed.contains("AC"));
assertTrue(Bookkeeper.namedStringsProcessed.contains("AD"));
assertTrue(Bookkeeper.namedStringsProcessed.contains("BC"));
assertTrue(Bookkeeper.namedStringsProcessed.contains("BD"));
assertEquals(4, Bookkeeper.namedStringsProcessed.size());
assertTrue(Bookkeeper.namedDataProcessed.contains("AC"));
assertTrue(Bookkeeper.namedDataProcessed.contains("AD"));
assertTrue(Bookkeeper.namedDataProcessed.contains("BC"));
assertTrue(Bookkeeper.namedDataProcessed.contains("BD"));
assertEquals(4, Bookkeeper.namedDataProcessed.size());
assertTrue(Bookkeeper.namedDataInstanceProcessed.contains("AC"));
assertTrue(Bookkeeper.namedDataInstanceProcessed.contains("AD"));
assertTrue(Bookkeeper.namedDataInstanceProcessed.contains("BC"));
assertTrue(Bookkeeper.namedDataInstanceProcessed.contains("BD"));
assertEquals(4, Bookkeeper.namedDataInstanceProcessed.size());
assertTrue(Bookkeeper.stringsProcessed.contains("AA"));
assertTrue(Bookkeeper.stringsProcessed.contains("AB"));
assertTrue(Bookkeeper.stringsProcessed.contains("AC"));
assertTrue(Bookkeeper.stringsProcessed.contains("AD"));
assertTrue(Bookkeeper.stringsProcessed.contains("BA"));
assertTrue(Bookkeeper.stringsProcessed.contains("BB"));
assertTrue(Bookkeeper.stringsProcessed.contains("BC"));
assertTrue(Bookkeeper.stringsProcessed.contains("BD"));
assertTrue(Bookkeeper.stringsProcessed.contains("CA"));
assertTrue(Bookkeeper.stringsProcessed.contains("CB"));
assertTrue(Bookkeeper.stringsProcessed.contains("CC"));
assertTrue(Bookkeeper.stringsProcessed.contains("CD"));
assertTrue(Bookkeeper.stringsProcessed.contains("DA"));
assertTrue(Bookkeeper.stringsProcessed.contains("DB"));
assertTrue(Bookkeeper.stringsProcessed.contains("DC"));
assertTrue(Bookkeeper.stringsProcessed.contains("DD"));
assertEquals(16, Bookkeeper.stringsProcessed.size());
assertTrue(Bookkeeper.dataProcessed.contains("AA"));
assertTrue(Bookkeeper.dataProcessed.contains("AB"));
assertTrue(Bookkeeper.dataProcessed.contains("AC"));
assertTrue(Bookkeeper.dataProcessed.contains("AD"));
assertTrue(Bookkeeper.dataProcessed.contains("BA"));
assertTrue(Bookkeeper.dataProcessed.contains("BB"));
assertTrue(Bookkeeper.dataProcessed.contains("BC"));
assertTrue(Bookkeeper.dataProcessed.contains("BD"));
assertTrue(Bookkeeper.dataProcessed.contains("CA"));
assertTrue(Bookkeeper.dataProcessed.contains("CB"));
assertTrue(Bookkeeper.dataProcessed.contains("CC"));
assertTrue(Bookkeeper.dataProcessed.contains("CD"));
assertTrue(Bookkeeper.dataProcessed.contains("DA"));
assertTrue(Bookkeeper.dataProcessed.contains("DB"));
assertTrue(Bookkeeper.dataProcessed.contains("DC"));
assertTrue(Bookkeeper.dataProcessed.contains("DD"));
assertEquals(16, Bookkeeper.dataProcessed.size());
assertTrue(Bookkeeper.dataInstanceProcessed.contains("AA"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("AB"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("AC"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("AD"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("BA"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("BB"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("BC"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("BD"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("CA"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("CB"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("CC"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("CD"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("DA"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("DB"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("DC"));
assertTrue(Bookkeeper.dataInstanceProcessed.contains("DD"));
assertEquals(16, Bookkeeper.dataInstanceProcessed.size());
assertTrue(Bookkeeper.integerProcessed.contains(1));
assertTrue(Bookkeeper.integerProcessed.contains(2));
assertTrue(Bookkeeper.integerProcessed.contains(3));
assertTrue(Bookkeeper.integerProcessed.contains(5));
assertEquals(4, Bookkeeper.integerProcessed.size());
}
}
================================================
FILE: jukito/src/test/java/org/jukito/AssistedInjectTest.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
/**
* Tests that new Guice 3.0 assisted injection works in Jukito.
*/
@RunWith(JukitoRunner.class)
public class AssistedInjectTest {
/**
* Guice test module.
*/
static class Module extends JukitoModule {
@Override
protected void configureTest() {
bindConstant().annotatedWith(Names.named("G")).to(6.673E-11);
install(new FactoryModuleBuilder().implement(Payment.class, RealPayment.class)
.build(PaymentFactory.class));
install(new FactoryModuleBuilder().implement(Star.class, StarImpl.class)
.build(StarFactory.class));
}
}
interface PaymentFactory {
Payment create(Date startDate, int amount);
}
interface Payment {
String format();
}
static class RealPayment implements Payment {
private final Date date;
private final int amount;
private final LocaleInfo localeInfo;
@Inject
RealPayment(@Assisted Date date, @Assisted int amount, LocaleInfo localeInfo) {
this.date = date;
this.amount = amount;
this.localeInfo = localeInfo;
}
@Override
public String format() {
String result = "Paid " + Integer.toString(amount);
if (localeInfo.isMoneySignBefore()) {
result = localeInfo.getMoneySign() + result;
} else {
result += localeInfo.getMoneySign();
}
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy", Locale.US);
formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
result += " on " + formatter.format(date);
return result;
}
}
interface LocaleInfo {
String getMoneySign();
boolean isMoneySignBefore();
}
interface StarFactory {
Star create(String name, double mass);
}
interface Star {
double getGravitationalConstant();
double getMass();
String getName();
}
static class StarImpl implements Star {
private final double gravitationalConstant;
private final double mass;
private final String name;
@Inject
StarImpl(@Named("G") Double gravitationalConstant, @Assisted double mass,
@Assisted String name) {
this.gravitationalConstant = gravitationalConstant;
this.mass = mass;
this.name = name;
}
@Override
public double getGravitationalConstant() {
return gravitationalConstant;
}
@Override
public double getMass() {
return mass;
}
@Override
public String getName() {
return name;
}
}
@Inject
StarFactory starFactory;
@Before
public void setup(LocaleInfo localeInfo) {
when(localeInfo.getMoneySign()).thenReturn("$");
when(localeInfo.isMoneySignBefore()).thenReturn(false);
}
@Test
public void testFactory(PaymentFactory factory) {
// GIVEN
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.US);
calendar.set(2011, 4, 24); // Month is 0-based
// WHEN
Payment payment = factory.create(calendar.getTime(), 50);
// THEN
assertEquals("Paid 50$ on 05/24/2011", payment.format());
}
@Test
public void testFactoryWithInjectedConstant() {
// WHEN
Star star = starFactory.create("Sun", 1.99E30);
// THEN
assertEquals("Sun", star.getName());
assertEquals(1.99E30, star.getMass(), 0.000001);
assertEquals(6.673E-11, star.getGravitationalConstant(), 0.000001);
}
}
================================================
FILE: jukito/src/test/java/org/jukito/AutoBindMocksDisabledTest.java
================================================
/*
* Copyright 2017 ArcBees 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 org.jukito;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import com.google.inject.PrivateModule;
import jakarta.inject.Inject;
/**
* Tests behavior of autoBindMocks property on UseModules annotation.
* NOTE: If autoBindMocks is true, this test will fail because SomeInterface will be auto bound
* to a mock and the binding will conflict with private module's binding
*/
@RunWith(JukitoRunner.class)
@UseModules(value = AutoBindMocksDisabledTest.MyModule.class, autoBindMocks = false)
public class AutoBindMocksDisabledTest {
/**
* Guice PrivateModule for testing.
*/
public static final class MyModule extends PrivateModule {
@Override
protected void configure() {
bind(ExposedClass.class);
bind(SomeInterface.class).to(NotAMock.class);
expose(ExposedClass.class);
}
}
/**
* Test Interface that will be auto mocked if autoBindMocks is set to true.
*/
public interface SomeInterface {
void doSomething();
}
/**
* When autoBindMocks is false, an instance of this type will be bound
* to SomeInterface from the PrivateModule.
*/
public static final class NotAMock implements SomeInterface {
@Override
public void doSomething() {
}
}
/**
* Class which injects either the automock or the concrete instance
* depending on the autoBindMocks property.
*/
public static final class ExposedClass {
private SomeInterface instance;
@Inject
ExposedClass(final SomeInterface instance) {
this.instance = instance;
}
SomeInterface getInstance() {
return instance;
}
}
@Test
public void testSomething(final ExposedClass clazz) throws Exception {
Assert.assertFalse(Mockito.mockingDetails(clazz).isMock());
Assert.assertFalse(Mockito.mockingDetails(clazz.getInstance()).isMock());
}
}
================================================
FILE: jukito/src/test/java/org/jukito/BindAnnotatedConcreteClassesTest.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.google.inject.name.Named;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
/**
* Test that annotated concrete classes can be correctly bound.
* See http://code.google.com/p/jukito/issues/detail?id=12
*/
@RunWith(JukitoRunner.class)
public class BindAnnotatedConcreteClassesTest {
/**
* Guice test module.
*/
static class Module extends JukitoModule {
@Override
protected void configureTest() {
bindNamed(ConcreteClass.class, "a").to(ConcreteClass.class).in(TestSingleton.class);
bindNamed(ConcreteClass.class, "b").to(ConcreteClass.class).in(TestSingleton.class);
bindNamed(ConcreteClass.class, "c").to(SubConcreteClass.class);
bindNamed(ConcreteClass.class, "d").to(SubConcreteClass.class);
bind(SubConcreteClass.class).in(TestSingleton.class);
bind(SubSubConcreteClass.class);
}
}
static class ConcreteClass {
}
static class SubConcreteClass extends ConcreteClass {
}
static class SubSubConcreteClass extends ConcreteClass {
}
@Test
public void testConcreteClassBoundToDifferentSingletons(@Named("a") ConcreteClass a,
@Named("b") ConcreteClass b) {
// THEN
assertNotSame(a, b);
}
@Test
public void testConcreteClassBoundToSameSingleton(@Named("c") ConcreteClass c,
@Named("d") ConcreteClass d) {
// THEN
assertSame(c, d);
}
@Test
public void testConcreteClassNoBoundAsSingleton(SubSubConcreteClass instance1,
SubSubConcreteClass instance2) {
// THEN
assertNotSame(instance1, instance2);
}
}
================================================
FILE: jukito/src/test/java/org/jukito/BindSpyInstanceTest.java
================================================
/**
* Copyright 2014 ArcBees 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 org.jukito;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
/**
* Bind spy instance test.
*/
@RunWith(JukitoRunner.class)
public class BindSpyInstanceTest {
/**
* Guice test module.
*/
static class Module extends JukitoModule {
@Override
protected void configureTest() {
bindSpy(SimpleClass.class, new SimpleClass("foo")).in(TestScope.SINGLETON);
}
}
static class SimpleClass {
private String arg0;
@SuppressWarnings("unused")
SimpleClass() {
this("default");
}
SimpleClass(String arg0) {
this.arg0 = arg0;
}
String getVal() {
return arg0;
}
}
@Test
public void testOneInvocation(SimpleClass simple) {
String value = simple.getVal();
assertEquals("foo", value);
verify(simple).getVal();
}
@Test
public void testNeverInvoked(SimpleClass simple) {
verify(simple, never()).getVal();
}
}
================================================
FILE: jukito/src/test/java/org/jukito/BindSpyTest.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.google.inject.Inject;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
/**
* Test that binding spy works correctly.
*/
@RunWith(JukitoRunner.class)
public class BindSpyTest {
/**
* Guice test module.
*/
static class Module extends JukitoModule {
@Override
protected void configureTest() {
bindSpy(SimpleClass.class).in(TestScope.SINGLETON);
}
}
interface CompositionMockA {
String test();
}
interface CompositionMockB {
String test();
}
static class SimpleClass {
@Inject
CompositionMockB mockB;
private CompositionMockA mockA;
@Inject
SimpleClass(CompositionMockA mockA) {
this.mockA = mockA;
}
String callTestMethodOnMock() {
mockA.test();
mockB.test();
return "Default string";
}
}
@Inject
CompositionMockA mockA;
@Inject
CompositionMockA mockB;
@Test
public void testStubbingSpiedInstance(SimpleClass simpleClass) {
// GIVEN
doReturn("Mocked string").when(simpleClass).callTestMethodOnMock();
// WHEN
String result = simpleClass.callTestMethodOnMock();
// THEN
assertEquals("Mocked string", result);
verify(mockA, never()).test();
verify(mockB, never()).test();
}
@Test
public void testNotStubbingSpiedInstance(SimpleClass simpleClass) {
// WHEN
String result = simpleClass.callTestMethodOnMock();
// THEN
verify(mockA).test();
verify(mockB).test();
assertEquals("Default string", result);
}
}
================================================
FILE: jukito/src/test/java/org/jukito/BindingToProviderTest.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.google.inject.Provides;
import jakarta.inject.Provider;
import static org.junit.Assert.assertEquals;
/**
* A test to make sure injecting a Provider in a @Provides method.
* See https://github.com/ArcBees/Jukito/issues/34
*/
@RunWith(JukitoRunner.class)
public class BindingToProviderTest {
/**
* Guice test module.
*/
public static class MyModule extends JukitoModule {
@Provides
public final MyClass getObject(Provider provider) {
return new MyClass("abc" + provider.get());
}
@Override
protected void configureTest() {
bind(String.class).toInstance("def");
}
}
static class MyClass {
private final String string;
MyClass(String string) {
this.string = string;
}
String getString() {
return string;
}
}
@Test
public void foo(MyClass obj) {
assertEquals("abcdef", obj.getString());
}
}
================================================
FILE: jukito/src/test/java/org/jukito/EDRunner.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.util.ArrayList;
import java.util.List;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.ParentRunner;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
/**
* Runner allows to run the all test methods in environment build upon different implementations
* via dedicated Injectors per test run.
*/
public class EDRunner extends ParentRunner {
private List runnersList;
public EDRunner(Class> testClass) throws Exception {
super(testClass);
runnersList = createJukitoRunners(testClass);
}
public List createJukitoRunners(Class> testClass) throws Exception {
List result = new ArrayList();
for (Injector injector : calculateInjectors(testClass)) {
JukitoRunner jukitoRunner = new JukitoRunner(testClass, injector);
result.add(jukitoRunner);
}
return result;
}
protected List calculateInjectors(Class> testClass)
throws IllegalAccessException, InstantiationException {
List result = new ArrayList();
EnvironmentDependentModules environmentDependentModules
= testClass.getAnnotation(EnvironmentDependentModules.class);
if (environmentDependentModules == null) {
throw new RuntimeException(EnvironmentDependentModules.class + " not found on test class");
}
for (Class edModuleClass : environmentDependentModules.value()) {
Injector i = buildInjector(edModuleClass, testClass);
result.add(i);
}
return result;
}
@Override
protected List getChildren() {
return runnersList;
}
@Override
protected Description describeChild(Object child) {
return ((JukitoRunner) child).getDescription();
}
@Override
protected void runChild(Object child, RunNotifier notifier) {
((JukitoRunner) child).run(notifier);
}
private Injector buildInjector(Class extends Module> edModuleClazz, final Class> testClass)
throws InstantiationException, IllegalAccessException {
final Module environmentDependentModule = edModuleClazz.newInstance();
final AbstractModule testModule = new AbstractModule() {
@Override
protected void configure() {
for (Module declaredModule : getDeclaredModulesForTest(testClass)) {
install(declaredModule);
}
install(environmentDependentModule);
}
};
JukitoModule finalModule = new JukitoModule() {
@Override
protected void configureTest() {
install(testModule);
}
};
BindingsCollector collector = new BindingsCollector(finalModule);
collector.collectBindings();
finalModule.setBindingsObserved(collector.getBindingsObserved());
return Guice.createInjector(finalModule);
}
// TODO refactor to common user module discovery method (JukitoRunner uses similar code now)
private Module[] getDeclaredModulesForTest(Class> testClass) {
UseModules useModules = testClass.getAnnotation(UseModules.class);
Class extends Module>[] moduleClasses = useModules.value();
final Module[] modules = new Module[moduleClasses.length];
for (int i = 0; i < modules.length; i++) {
try {
modules[i] = moduleClasses[i].newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return modules;
}
}
================================================
FILE: jukito/src/test/java/org/jukito/EnvironmentDependentComponent.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
/**
* Sample interface which have implementations bound in different guice context (Injectors).
*/
public interface EnvironmentDependentComponent {
void hello();
}
================================================
FILE: jukito/src/test/java/org/jukito/ExternalSingleton.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import com.google.inject.Singleton;
/**
* A class annotated with @Singleton not defined within test class.
*/
@Singleton
public class ExternalSingleton {
}
================================================
FILE: jukito/src/test/java/org/jukito/ForceMockTest.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
/**
* Test that the {@link org.jukito.JukitoModule#forceMock} method works as expected.
*/
@RunWith(JukitoRunner.class)
public class ForceMockTest {
/**
* Guice test module.
*/
static class Module extends JukitoModule {
@Override
protected void configureTest() {
forceMock(Base1.class);
forceMock(Child21.class);
forceMock(Child311.class);
}
}
interface Base1 {
int t1();
}
static class Child11 implements Base1 {
public int t1() {
return 11;
}
}
static class Child111 extends Child11 {
public int t1() {
return 111;
}
}
static class Child12 implements Base1 {
public int t1() {
return 12;
}
}
interface Base2 {
int t2();
}
static class Child21 implements Base2 {
public int t2() {
return 21;
}
}
static class Child211 extends Child21 {
public int t2() {
return 211;
}
}
static class Child22 implements Base2 {
public int t2() {
return 22;
}
}
interface Base3 {
int t3();
}
static class Child31 implements Base3 {
public int t3() {
return 31;
}
}
static class Child311 extends Child31 {
public int t3() {
return 311;
}
}
@Test
public void injectForceMock(
Base1 base1,
Child11 child11,
Child111 child111,
Child12 child12,
Base2 base2,
Child21 child21,
Child211 child211,
Child22 child22,
Base3 base3,
Child31 child31,
Child311 child311) {
verify(base1, never()).t1();
verify(child11, never()).t1();
verify(child111, never()).t1();
verify(child12, never()).t1();
verify(base2, never()).t2();
verify(child21, never()).t2();
verify(child211, never()).t2();
assertEquals(22, child22.t2());
verify(base3, never()).t3();
assertEquals(31, child31.t3());
verify(child311, never()).t3();
}
}
================================================
FILE: jukito/src/test/java/org/jukito/GeneralTest.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.util.logging.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
/**
* Test various general behaviors.
*/
@RunWith(JukitoRunner.class)
public class GeneralTest {
/**
* Guice test module.
*/
static class Module extends JukitoModule {
@Override
protected void configureTest() {
bindConstant().annotatedWith(OneHundred.class).to(100);
bindConstant().annotatedWith(Names.named("200")).to(200);
bindConstant().annotatedWith(Names.named("HelloWorld")).to("Hello World!");
bindConstant().annotatedWith(Names.named("500L")).to(500L);
bindConstant().annotatedWith(Names.named("true")).to(true);
bindConstant().annotatedWith(Names.named("3.1415")).to(3.1415);
bindConstant().annotatedWith(Names.named("2.718f")).to(2.718f);
bindConstant().annotatedWith(Names.named("short8")).to((short) 8);
bindConstant().annotatedWith(Names.named("'a'")).to('a');
bindConstant().annotatedWith(Names.named("IntegerClass")).to(Integer.class);
bindConstant().annotatedWith(Names.named("VALUE1")).to(MyEnum.VALUE1);
bindConstant().annotatedWith(Names.named("VALUE2")).to(MyEnum.VALUE2);
bind(MyInteger.class).annotatedWith(OneHundred.class).toInstance(new MyIntegerImpl(100));
bind(MyInteger.class).annotatedWith(Names.named("200")).toInstance(new MyIntegerImpl(200));
bind(Key.get(TestClass.class, Value3.class)).toInstance(new TestClass(MyEnum.VALUE3));
bind(Key.get(TestClass.class, Names.named("VALUE2"))).to(TestClass.class).in(
TestSingleton.class);
bind(new TypeLiteral>() {
}).in(TestScope.SINGLETON);
bind(new TypeLiteral>() {
}).to(
ParameterizedTestClassDouble.class).in(TestScope.SINGLETON);
bindNamedMock(ClassWithUninstanciableDependency3.class, "UninstanciableDependency3a");
bind(ClassWithUninstanciableDependency3.class).annotatedWith(
Names.named("UninstanciableDependency3b")).toProvider(MyMockProvider3b.class);
bind(ClassWithUninstanciableDependency3.class).annotatedWith(
Names.named("UninstanciableDependency3c")).toProvider(Key.get(MyMockProvider3c.class));
try {
bind(ParameterizedTestClassString.class).toConstructor(
ParameterizedTestClassString.class.getConstructor(String.class));
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
// TODO: Try to bind a mock logger once Issue 9 is solved.
// bindMock(Logger.class);
}
}
interface MyInteger {
int getValue();
}
static class MyIntegerImpl implements MyInteger {
private final int value;
MyIntegerImpl(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
}
enum MyEnum {
VALUE1,
VALUE2,
VALUE3
}
static class TestClass {
private final MyEnum value;
@Inject
TestClass(@Named("VALUE2") MyEnum value) {
this.value = value;
}
}
static class ParameterizedTestClass {
final T value;
@Inject
ParameterizedTestClass(@Named("200") T value) {
this.value = value;
}
}
static class ParameterizedTestClassDouble extends ParameterizedTestClass {
@Inject
ParameterizedTestClassDouble() {
super(10.0);
}
}
public static class ParameterizedTestClassString extends ParameterizedTestClass {
ParameterizedTestClassString() {
super("default constructor");
}
public ParameterizedTestClassString(@Named("HelloWorld") String value) {
super(value);
}
}
static class TestClassWithMethodInjection {
private int value;
@Inject
TestClassWithMethodInjection(@OneHundred Integer value) {
this.value = value;
}
@Inject
public void setValue(@Named("200") Integer value) {
this.value = value;
}
}
interface NonBoundInterface {
int getValue();
}
static class TestClassWithOptionalInjection {
private int value;
@Inject
TestClassWithOptionalInjection(@OneHundred Integer value) {
this.value = value;
}
@Inject(optional = true)
public void setValue(NonBoundInterface obj) {
value = obj.getValue(); // Should never be called, NonBoundInterface should not be mocked
}
}
// This class will cause an error if bound
static class UninstanciableClass {
private UninstanciableClass() {
}
public int getValue() {
return 360;
}
}
@TestMockSingleton
static class ClassWithUninstanciableDependency1 {
private final UninstanciableClass dependency;
@Inject
ClassWithUninstanciableDependency1(UninstanciableClass dependency) {
this.dependency = dependency;
}
public int getValue() {
return 42;
}
public UninstanciableClass getDependency() {
return dependency;
}
}
abstract static class ClassWithUninstanciableDependency2 {
@Inject
ClassWithUninstanciableDependency2(UninstanciableClass dependency) {
}
public int getValue() {
return 42;
}
}
static class ClassWithUninstanciableDependency3 {
@Inject
ClassWithUninstanciableDependency3(UninstanciableClass dependency) {
}
public int getValue() {
return 42;
}
}
static class MyMockProvider3b extends MockProvider {
@Inject
MyMockProvider3b() {
super(ClassWithUninstanciableDependency3.class);
}
}
static class MyMockProvider3c extends MockProvider {
@Inject
MyMockProvider3c() {
super(ClassWithUninstanciableDependency3.class);
}
}
static class TestGenericClassInjectedWithTypeLiteral {
private final Class super U> injectedType;
@Inject
TestGenericClassInjectedWithTypeLiteral(TypeLiteral typeLiteral) {
injectedType = typeLiteral.getRawType();
}
public Class super U> getInjectedType() {
return injectedType;
}
}
@Test
public void testConstantInjection(
@OneHundred Integer oneHundred,
@Named("200") Integer twoHundred,
@Named("HelloWorld") String helloWorld,
@Named("500L") long fiveHundred,
@Named("3.1415") double pi,
@Named("2.718f") float e,
@Named("short8") short eight,
@Named("'a'") char a,
@SuppressWarnings("rawtypes")
@Named("IntegerClass") Class integerClass,
@Named("VALUE1") MyEnum value1) {
assertEquals(100, (int) oneHundred);
assertEquals(200, (int) twoHundred);
assertEquals("Hello World!", helloWorld);
assertEquals(500L, fiveHundred);
assertEquals(3.1415, pi, 0.0000001);
assertEquals(2.718f, e, 0.00001);
assertEquals(8, eight);
assertEquals('a', a);
assertEquals(Integer.class, integerClass);
assertEquals(MyEnum.VALUE1, value1);
}
@Test
public void testInjectBoundWithKeys(
@Value3 TestClass testClassValue3,
@Named("VALUE2") TestClass testClassValue2,
@OneHundred MyInteger testMyInteger100,
@Named("200") MyInteger testMyInteger200) {
assertEquals(MyEnum.VALUE3, testClassValue3.value);
assertEquals(MyEnum.VALUE2, testClassValue2.value);
assertEquals(100, testMyInteger100.getValue());
assertEquals(200, testMyInteger200.getValue());
}
@Test
public void testParameterizedInjection1(
ParameterizedTestClass testClass) {
assertEquals(200, (int) testClass.value);
}
@Test
public void testParameterizedInjection2(
ParameterizedTestClass testClass) {
assertEquals(10.0, (double) testClass.value, 0.0000001);
}
@Test
public void testMethodInjection(
TestClassWithMethodInjection testClass) {
assertEquals(200, testClass.value);
}
@Test
public void testOptionalInjection(
TestClassWithOptionalInjection testClass) {
assertEquals(100, testClass.value);
}
@Test
public void testInjectingMockShouldNotInstantiateDependencies1(
ClassWithUninstanciableDependency1 testClass) {
assertEquals(42, testClass.getValue());
verify(testClass.getDependency(), never()).getValue();
}
@Test
public void testInjectingMockShouldNotInstantiateDependencies2(
ClassWithUninstanciableDependency2 testClass) {
verify(testClass, never()).getValue();
}
@Test
public void testInjectingMockShouldNotInstantiateDependencies3a(
@Named("UninstanciableDependency3a") ClassWithUninstanciableDependency3 testClass) {
verify(testClass, never()).getValue();
}
@Test
public void testInjectingMockShouldNotInstantiateDependencies3b(
@Named("UninstanciableDependency3b") ClassWithUninstanciableDependency3 testClass) {
verify(testClass, never()).getValue();
}
@Test
public void testInjectingMockShouldNotInstantiateDependencies3c(
@Named("UninstanciableDependency3c") ClassWithUninstanciableDependency3 testClass) {
verify(testClass, never()).getValue();
}
@Test
public void testInjectingClassInjectedWithTypeLiteralShouldWork(
TestGenericClassInjectedWithTypeLiteral testClass) {
assertEquals(String.class, testClass.getInjectedType());
}
@Test
public void testInjectingTypeLiteralShouldWork(
TypeLiteral typeLiteral) {
assertEquals(Integer.class, typeLiteral.getRawType());
}
@Test
public void testInjectingInjectorShouldWork(
Injector injector) {
}
@Test
public void testInjectingLoggerShouldWork(
Logger logger) {
}
@Test
public void testInjectingStageShouldWork(
Stage stage) {
}
@Test
public void testInjectingMembersInjectorShouldWork(
MembersInjector memberInjector) {
}
@Test
public void toConstructorInjectionShouldWork(ParameterizedTestClassString object) {
assertEquals("Hello World!", object.value);
}
}
================================================
FILE: jukito/src/test/java/org/jukito/InnerClassTest.java
================================================
/**
* Copyright 2014 ArcBees 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 org.jukito;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.google.inject.ConfigurationException;
import com.google.inject.Inject;
import static org.junit.Assert.assertEquals;
/**
* Test to ensure that injecting inner classes throw a ConfigurationException, instead
* of simply injecting a mock. Additionally, test that injecting static inner classes
* still work properly.
*/
@RunWith(JukitoRunner.class)
public class InnerClassTest {
/**
* Test module, just bind anything to make sure regular injections still work properly.
*/
public static class Module extends JukitoModule {
@Override
protected void configureTest() {
bind(String.class).toInstance("hello world!");
}
}
/**
* Dummy inner class with a single inject.
*/
class InnerClass {
@Inject
String test;
public String toString() {
return test;
}
}
/**
* Dummy static inner class with a single inject.
*/
static class StaticInnerClass {
@Inject
String test;
public String toString() {
return test;
}
}
/**
* Verify that when you try to inject an inner class, a ConfigurationException is thrown.
*
* @param klass
*/
@Test(expected = ConfigurationException.class)
public void testInnerClass(InnerClass klass) {
assertEquals("hello world!", klass.toString());
}
/**
* Verify that when you try to inject a static inner class, everything works properly.
*
* @param klass
*/
@Test
public void testStaticInnerClass(StaticInnerClass klass) {
assertEquals("hello world!", klass.toString());
}
}
================================================
FILE: jukito/src/test/java/org/jukito/InstallTest.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.name.Named;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
/**
* Test exercising Guice's install() mechanism.
*/
@RunWith(JukitoRunner.class)
public class InstallTest {
/**
* Guice test module, containing two submodules.
*/
static class Module extends JukitoModule {
public class FooModule extends AbstractModule {
@Override
protected void configure() {
bind(Foo.class).to(FooImpl.class);
}
}
public class BarModule extends AbstractModule {
@Override
protected void configure() {
bind(Bar.class).to(BarImpl.class);
}
}
@Override
protected void configureTest() {
install(new FooModule());
install(new BarModule());
bindNamedMock(Foo.class, "ten").in(TestSingleton.class);
}
}
interface Foo {
int calc();
}
static class FooImpl implements Foo {
private final Provider barProvider;
private final Foo ten;
@Inject
FooImpl(Provider barProvider, @Named("10") Foo ten) {
this.barProvider = barProvider;
this.ten = ten;
}
@Override
public int calc() {
return this.barProvider.get().calc() + ten.calc();
}
}
interface Bar {
int calc();
}
static class BarImpl implements Bar {
@Override
public int calc() {
return 5;
}
}
@Inject
Foo foo;
@Before
public void setup(@Named("10") Foo ten) {
when(ten.calc()).thenReturn(10);
}
@Test
public void installingModuleWorks(Bar bar) {
assertEquals(15, foo.calc());
assertEquals(5, bar.calc());
}
}
================================================
FILE: jukito/src/test/java/org/jukito/ModuleWithProvidesMethods.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import static org.mockito.Mockito.mock;
/**
* Simple module with factory method.
*/
public class ModuleWithProvidesMethods extends AbstractModule {
@Provides
@TestSingleton
SomeTestClass create() {
SomeTestClass mock = mock(SomeTestClass.class);
mock.someInitMethod();
return mock;
}
@Override
protected void configure() {
}
}
================================================
FILE: jukito/src/test/java/org/jukito/NoModuleTest.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertSame;
import static org.mockito.Mockito.verify;
/**
* Test to ensure injection works well without a module.
*/
@RunWith(JukitoRunner.class)
public class NoModuleTest {
interface MyMockSingleton {
int dummy();
}
@TestMockSingleton
interface MyAnnotatedMockSingleton {
int dummy();
}
static class MySingleton {
}
@TestSingleton
static class MyAnnotatedSingleton {
}
@TestEagerSingleton
static class MyAnnotatedEagerSingleton {
}
@Test
public void testMockSingleton(MyMockSingleton a, MyMockSingleton b) {
assertSame(a, b);
a.dummy();
verify(a).dummy();
verify(b).dummy();
}
@Test
public void testAnnotatedMockSingleton(MyAnnotatedMockSingleton a,
MyAnnotatedMockSingleton b) {
assertSame(a, b);
a.dummy();
verify(a).dummy();
verify(b).dummy();
}
@Test
public void testSingleton(MySingleton a, MySingleton b) {
assertSame(a, b);
}
@Test
public void testAnnotatedSingleton(MyAnnotatedSingleton a, MyAnnotatedSingleton b) {
assertSame(a, b);
}
@Test
public void testAnnotatedEagerSingleton(MyAnnotatedEagerSingleton a,
MyAnnotatedEagerSingleton b) {
assertSame(a, b);
}
}
================================================
FILE: jukito/src/test/java/org/jukito/OldStyleAssistedInjectTest.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.FactoryProvider;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
import static org.junit.Assert.assertEquals;
/**
* Test that make sure old-style Guice 2.0 assisted injection is supported.
*/
@SuppressWarnings("deprecation")
@RunWith(JukitoRunner.class)
public class OldStyleAssistedInjectTest {
/**
* Guice test module.
*/
public static class Module extends JukitoModule {
@Override
protected void configureTest() {
bindConstant().annotatedWith(Names.named("moneySymbol")).to("$");
bindNamed(PaymentFactory.class, "factory1").toProvider(
FactoryProvider.newFactory(PaymentFactory.class, RealPayment1.class));
bindNamed(PaymentFactory.class, "factory2").toProvider(
FactoryProvider.newFactory(PaymentFactory.class, RealPayment2.class));
bind(PaymentAmountFactory.class).toProvider(
FactoryProvider.newFactory(PaymentAmountFactory.class, RealPaymentAmount.class));
bind(Amount.class).toInstance(new Amount() {
public String toString() {
return "An amount of 10.00 ";
}
});
}
}
interface PaymentFactory {
Payment create(int amount);
}
interface PaymentAmountFactory {
Payment create(Amount amount);
}
interface Payment {
String getPayment();
}
static class RealPayment1 implements Payment {
private final String moneySymbol;
private final int amount;
@Inject
RealPayment1(@Named("moneySymbol") String moneySymbol, @Assisted int amount) {
this.moneySymbol = moneySymbol;
this.amount = amount;
}
@Override
public String getPayment() {
return Integer.toString(amount) + ".00" + moneySymbol;
}
}
static class RealPayment2 implements Payment {
private final int amount;
@Inject
RealPayment2(@Assisted int amount) {
this.amount = amount;
}
@Override
public String getPayment() {
return Integer.toString(amount) + " dollars";
}
}
interface Amount {
String toString();
}
// Interface Configuration should be mocked because it is a dependency
// of RealPaymentAmout. The mocked version of {@ocde shouldAlwaysHideAmounts()}
// will always return {@code false}.
interface Configuration {
boolean shouldAlwaysHideAmounts();
}
// Class InjectedClass should be bound automatically
// because it is a dependency of RealPaymentAmount
static class InjectedClass {
@Inject
@Named("moneySymbol")
String moneySymbol;
}
static class RealPaymentAmount implements Payment {
private final Configuration configuration;
private final InjectedClass injectedClass;
private final Amount amount;
@Inject
RealPaymentAmount(Configuration configuration,
InjectedClass injectedClass,
@Assisted Amount amount) {
this.configuration = configuration;
this.injectedClass = injectedClass;
this.amount = amount;
}
@Override
public String getPayment() {
if (configuration.shouldAlwaysHideAmounts()) {
return "xxxxxxx " + injectedClass.moneySymbol;
}
return amount.toString() + injectedClass.moneySymbol;
}
}
@Inject
@Named("factory1")
PaymentFactory factory1;
@Test
public void shouldInjectFactoryInClass() {
// WHEN
Payment payment = factory1.create(20);
// THEN
assertEquals("20.00$", payment.getPayment());
}
@Test
public void shouldInjectFactoryAsParameter(@Named("factory2") PaymentFactory factory2) {
// WHEN
Payment payment = factory2.create(30);
// THEN
assertEquals("30 dollars", payment.getPayment());
}
@Test
public void shouldInjectFactoryWithInterfacesAsParameter(
PaymentAmountFactory factoryString,
Amount amount) {
// WHEN
Payment payment = factoryString.create(amount);
// THEN
assertEquals("An amount of 10.00 $", payment.getPayment());
}
}
================================================
FILE: jukito/src/test/java/org/jukito/OneHundred.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import com.google.inject.BindingAnnotation;
/**
* An annotation used in a test class.
*/
@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
public @interface OneHundred {
}
================================================
FILE: jukito/src/test/java/org/jukito/ParentClassInnerClassModuleDiscoveryTest.java
================================================
/*
* Copyright 2017 ArcBees 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 org.jukito;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Test which ensures that nested TestModules in parent classes are discovered by the JukitoRunner.
*/
@RunWith(JukitoRunner.class)
public class ParentClassInnerClassModuleDiscoveryTest extends SampleParentTestClassWithInnerTestModule {
private static final AtomicInteger numberOfTestRuns = new AtomicInteger(0);
@AfterClass
public static void afterClass() throws Exception {
Assert.assertEquals(2, numberOfTestRuns.get());
}
@Test
public void testSomething(@All final String bindingsFromParent) throws Exception {
numberOfTestRuns.incrementAndGet();
}
}
================================================
FILE: jukito/src/test/java/org/jukito/ParentTestClassBase.java
================================================
/**
* Copyright 2013 ArcBees 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 org.jukito;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import com.google.inject.Inject;
import com.google.inject.Provider;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
/**
* This parent test class is used by {@link ParentTestClassTest}.
*/
@Ignore("Tests in this base class are not meant to be run independantly.")
public class ParentTestClassBase {
/**
* This should be automatically injected in the child class.
*/
@TestSingleton
static class SingletonDefinedInParent {
private String value = "SingletonDefinedInParentValue";
public String getValue() {
return value;
}
}
/**
* This should be automatically injected in the child class.
*/
@TestMockSingleton
interface MockSingletonDefinedInParent {
void mockSingletonMethod();
}
interface DummyInterface {
String getDummyValue();
}
interface DummyInterfaceUsedOnlyInParent1 {
String getDummyValue();
}
interface DummyInterfaceUsedOnlyInParent2 {
String getDummyValue();
}
interface DummyInterfaceUsedOnlyInParent3 {
String getDummyValue();
}
static class DummyClassUsedOnlyInParent1 {
}
static class DummyClassUsedOnlyInParent2 {
}
static class DummyClassUsedOnlyInParent3 {
}
@Inject
protected Provider dummyProvider;
@Inject
protected MockSingletonDefinedInParent mockSingletonDefinedInParent;
/**
* This class keeps track of what happens in all the tests run in this
* class and its child. It's used to make sure all expected tests are called.
*/
protected static class Bookkeeper {
static boolean parentTestShouldRunExecuted;
}
@Test
public void parentTestShouldRun() {
Bookkeeper.parentTestShouldRunExecuted = true;
}
@Test
public void interfaceBoundInChildIsInjectedInParent() {
assertEquals("DummyValue", dummyProvider.get().getDummyValue());
}
@Test
public void interfaceBoundInChildIsInjectedInParentTestMethod(
DummyInterface dummyInterface) {
assertEquals("DummyValue", dummyInterface.getDummyValue());
}
@Test
public void interfaceUsedInParentTestMethodShouldBeMockedAsTestSingleton(
Provider provider) {
// Following should not crash
verify(provider.get(), never()).getDummyValue();
assertSame(provider.get(), provider.get());
}
@Test
public void concreteClassUsedInParentTestMethodShouldBeBoundAsTestSingleton(
Provider