I'm using Cucumber with Guice as DI.
I've encountered following problem:
I've got one step i.e.
class MyStep() {
#Inject
private MyService myService;
#Given("Some acction happen")
public void sthHappen() {
myService.doSth();
}
}
And I've got this class to run it as JUnit test
#RunWith(Cucumber.class)
#CucumberOptions(...)
public class MyTest {
}
There is a
class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(MyService.class).to(MyFirstService.class);
}
}
which is used by my MyInjectorSource
I define cucumber.properties where I define guice.injector-source=MyInjectorSource;
There is also a feature file with scenario.
Everything is working for now.
And no i would like to run MyStep step with other MyService implementation (of course I don't wont to duplicate code of MyStep)
I define a new feature file with new scenarios, and new Test class
#RunWith(Cucumber.class)
#CucumberOptions(...)
public class MyOtherTest {
}
And now I've tried to create another InjectorSource but I was not able to configure it.
Solution which I've found is using custom Junit4 runner inheriting from original Cucumber runner and changing its createRuntime method.
Latest cucumber-guice 1.2.5 uses few stages to create injector and unfortunately it uses global variable cucumber.runtime.Env.INSTANCE. This variable is populated from cucumber.properties and System.getProperties.
Flow is:
Cucumber runner scans available backends (in my setup it is cucumber.runtime.java.JavaBackend)
One of JavaBackend constructor loads available ObjectFactory (in my setup it is cucumber.runtime.java.guice.impl.GuiceFactory)
GuiceFactory via InjectorSourceFactory checks Env.INSTANCE, it will create custom InjectorSource or default injector
Ideally cucumber should pass its 'RuntimeOptions` created at start to backend and InjectorSource but unfortunately it doesn't and uses global variable. It is not easy create patch like this one so my solution simplifies this approach and directly create InjectorSource in custom runner by reading new annotation.
public class GuiceCucumberRunner extends Cucumber {
public GuiceCucumberRunner(Class<?> clazz) throws InitializationError, IOException {
super(clazz);
}
#Override
protected Runtime createRuntime(ResourceLoader resourceLoader, ClassLoader classLoader, RuntimeOptions runtimeOptions) throws InitializationError, IOException {
Runtime result = new Runtime(resourceLoader, classLoader, Arrays.asList(createGuiceBackend()), runtimeOptions);
return result;
}
private JavaBackend createGuiceBackend() {
GuiceCucumberOptions guiceCucumberOptions = getGuiceCucumberOptions();
InjectorSource injectorSource = createInjectorSource(guiceCucumberOptions.injectorSource());
ObjectFactory objectFactory = new GuiceFactory(injectorSource.getInjector());
JavaBackend result = new JavaBackend(objectFactory);
return result;
}
private GuiceCucumberOptions getGuiceCucumberOptions() {
GuiceCucumberOptions guiceCucumberOptions = getTestClass().getJavaClass().getAnnotation(GuiceCucumberOptions.class);
if (guiceCucumberOptions == null) {
String message = format("Suite class ''{0}'' is missing annotation GuiceCucumberOptions", getTestClass().getJavaClass());
throw new CucumberException(message);
}
return guiceCucumberOptions;
}
private InjectorSource createInjectorSource(Class<? extends InjectorSource> clazz) {
try {
return clazz.newInstance();
} catch (Exception e) {
String message = format("Instantiation of ''{0}'' failed. InjectorSource must have has a public zero args constructor.", clazz);
throw new InjectorSourceInstantiationFailed(message, e);
}
}
static class GuiceFactory implements ObjectFactory {
private final Injector injector;
GuiceFactory(Injector injector) {
this.injector = injector;
}
#Override
public boolean addClass(Class<?> clazz) {
return true;
}
#Override
public void start() {
injector.getInstance(ScenarioScope.class).enterScope();
}
#Override
public void stop() {
injector.getInstance(ScenarioScope.class).exitScope();
}
#Override
public <T> T getInstance(Class<T> clazz) {
return injector.getInstance(clazz);
}
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE })
public #interface GuiceCucumberOptions {
Class<? extends InjectorSource> injectorSource();
}
#RunWith(GuiceCucumberRunner.class)
#GuiceCucumberOptions(injectorSource = MyInjector.class)
#CucumberOptions(
...
)
public class Suite {
}
I needed to copy GuiceFactory because it doesn't exposes normal constructor (!)
Related
I have a Spring Boot application where an interface has a constraint:
#Constraint(
validatedBy = {
MyValidator.class
})
public #interface MyInterface {
...
}
I'm using Togglz to enable/disable some features and one class where I want to implement some Togglz code is in MyValidator.
public class MyValidator
implements MyInterface<
MyInterface, TDLDetails> {
private FeatureManager featureManager;
public static final Feature FEATURE_ONE =
new NamedFeature("FEATURE_ONE ");
public MyValidator(FeatureManager featureManager) {
this.featureManager = featureManager;
}
#Override
public void initialize(MyInterface arg0) {}
#Override
public boolean isValid(TDLDetails tdlDetails, ConstraintValidatorContext ctx)
{
if (!featureManager.isActive(FEATURE_ONE)) {
if (tdlDetails.getType().equals(TDLType.ANA)) {
return (tdlDetails.getPlaceOfIssue() != null);
}
}
return true;
}
}
Am I wrong to have the parameterized constructor? It seems I need it for Togglz but I'm not sure how it should be used by #Constraint if it takes a parameter. What's the correct way to do this?
Guice newbie here, with a complicated scenario.
My company has a large number of constants of a given type (let's call them Thingy) that belong to different teams and are maintained in different parts of our application. However, we need to have a central registry that knows about all of them (let's call this the ThingyService). I am writing a base module that teams can either extend or install, with the purpose of allowing a team to register their Thingys, and giving them access to the ThingyService. This module takes as parameter a list of classes from which I can extract the Thingy constants, this part is working fine.
What I don't understand is how I can a) make each module know about each other module's list of Thingys and b) how I can create my ThingyService as a singleton that contains all of my Thingys. I have experimented with shared static state and with ThreadLocals, but I keep either breaking tests or breaking my main (play) application. In my naive understanding of Guice, I think I need a MultiBinder for the Thingys, but I don't see how I can share that between modules. Here's what I'd like to do:
class ThingyModule extends AbstractModule{
final Set<Class<?>> myThingyClasses; // this is populated in the constructor
private Set<Thingy> extractThingiesFromThingyClasses(){
// I have this working
}
#Provides #Singleton ThingyService thingyService(
Set<Thingy> thingys // all thingys, from all such modules
){
return new ThingyService(thingys);
}
protected void configure(){
extractThingiesFromThingyClasses().forEach(thingy->
// bind thingy to a global MultiBinder?
);
}
}
How can I make my ThingyService unique and global, with all the Thingys from the entire application? Note: I don't necessarily need my Thingys to be managed by Guice, the only place I need them is in ThingyService. Also, this is a play / scala application if that makes a difference, but my ThingyModule code lives in a library written in Java.
It turns out I omitted one important detail, Thingy has a type parameter, it's actually Thingy<T>, and that's the reason it didn't work before. By cheating and registering Thingy as raw type, and then also injecting it as raw type, I got it to work.
Here is a complete working example using JUnit 5 and AssertJ:
class ThingyModuleTest {
static class Thingy<T>{
private final T value;
Thingy(final T value) {this.value = value;}
#Override public boolean equals(final Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
final Thingy<?> thingy = (Thingy<?>) o; return Objects.equals(value, thingy.value); }
#Override public int hashCode() { return Objects.hash(value); }
}
#Singleton
static class ThingyService{
final Set<Thingy<?>> thingies;
#SuppressWarnings({"unchecked", "rawtypes"}) #Inject
ThingyService(Set<Thingy> thingies) {
this.thingies = ImmutableSet.copyOf((Set)thingies);
}
public Set<Thingy<?>> getThingies() { return thingies; }
}
abstract static class ThingyModule extends AbstractModule {
private final Set<Class<?>> classesToScan;
public ThingyModule(Class<?>... classes) {
this.classesToScan = ImmutableSet.copyOf(classes);
}
private Set<Thingy<?>> scanForThingies(){
return classesToScan.stream()
.flatMap(c-> Arrays.stream(c.getDeclaredFields()))
.filter(f->f.getType().isAssignableFrom(Thingy.class))
.filter(f-> Modifier.isStatic(f.getModifiers())&&Modifier.isFinal(f.getModifiers()))
.map(this::readThingy)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toSet());
}
#SuppressWarnings("unchecked")
private Optional<Thingy<?>> readThingy(final Field field) {
try{
field.setAccessible(true);
return Optional.ofNullable(field.get(null))
.filter(Thingy.class::isInstance)
.map(Thingy.class::cast);
} catch (IllegalAccessException e) { return Optional.empty(); }
}
#Override protected void configure() {
bind(ThingyService.class);
#SuppressWarnings("rawtypes") Multibinder<Thingy> multibinder = Multibinder.newSetBinder(binder(), Thingy.class);
scanForThingies().forEach(thingy -> multibinder.addBinding().toInstance(thingy));
}
}
static class ThingyModule1 extends ThingyModule {
public ThingyModule1() { super(Thingies1.class); }
static class Thingies1{
static final Thingy<Boolean> BooleanThingy = new Thingy<>(true);
static final Thingy<Integer> IntThingy = new Thingy<>(123);
}
}
static class ThingyModule2 extends ThingyModule {
public ThingyModule2() { super(Thingies2.class); }
static class Thingies2{
static final Thingy<String> StringThingy = new Thingy<>("hello");
static final Thingy<Long> LongThingy = new Thingy<>(123L);
}
}
#Test void validateThingyService() {
ThingyService thingyService = Guice.createInjector(new ThingyModule1(), new ThingyModule2())
.getProvider(ThingyService.class)
.get();
assertThat(thingyService).isNotNull()
.extracting(ts -> ImmutableList.copyOf(ts.getThingies()))
.asList()
.containsExactlyInAnyOrder(BooleanThingy, IntThingy, StringThingy, LongThingy);
}
}
I will mark this answer as accepted until somebody else provides a more idiomatic one.
I have Neo4j unmanaged extension. I want some services to be created as singletons and be available via #Context in my resources.
Something like this:
#Path("/example")
public class ExampleResource {
public ExampleResource(#Context CostlyService costlyService) { // <<---
// use it here
}
}
How this can be achieved?
Neo4j has PluginLifecycle interface that give us possibility to hook into Neo4j server lifecycle and provide our own services for injection blog post.
So, we have service. Let's take this one as example:
public interface CostlyService {
}
public class CostlyServiceImpl implements CostlyService {
public CostlyService() {
// a LOT of work done here
}
//...
}
Now we need to make our own PluginLifecycle implementation:
public class ExamplePluginLifecycle implements PluginLifecycle {
#Override
public Collection<Injectable<?>> start(GraphDatabaseService graphDatabaseService,
Configuration config) {
final List<Injectable<?>> injectables = new ArrayList<>();
return injectables;
}
#Override
public void stop() {
}
}
As you see, injectable list is empty for now. We will add our service there soon.
Important: you must register your PluginLifecycle implementation, so it will be available via SPI:
// file: META-INF/services/org.neo4j.server.plugins.PluginLifecycle
my.company.extension.ExamplePluginLifecycle
This will make your PluginLifecycle discoverable by Neo4j server.
Now we need to create actual injectable. Let's write implementation for Injectable interface:
public final class TypedInjectable<T> implements Injectable<T> {
private final T value;
private final Class<T> type;
private TypedInjectable(final T value, final Class<T> type) {
this.value = value;
this.type = type;
}
public static <T> TypedInjectable<T> injectable(final T value, final Class<T> type) {
return new TypedInjectable<>(value, type);
}
#Override
public T getValue() {
return value;
}
#Override
public Class<T> getType() {
return type;
}
}
This will serve as simple container for our service. Usage:
import static my.company.extension.TypedInjectable.injectable;
injectable(new CostlyServiceImpl(), CostlyService.class);
Now we can add our injectable into PluginLifecycle.
#Override
public Collection<Injectable<?>> start(GraphDatabaseService graphDatabaseService,
Configuration config) {
final List<Injectable<?>> injectables = new ArrayList<>();
injectables.add(injectable(new CostlyServiceImpl, CostlyService.class)); // <<---
return injectables;
}
After this change our CostlyService will be available for our resources via #Context:
#Path("/example")
public class ExampleResource {
public ExampleResource(#Context CostlyService costlyService) {
// use it here
}
// ...
}
Tip: keep your PluginLifecycle's in same package or in subpackage with your resources.
In junit4 I want to execute specific test methods from different classes i.e want create a test suite with specific test methods from different classes.
Lets say I have 2 classes:
public class Test_Login {
#Test
public void test_Login_001(){
System.out.println("test_Login_001");
}
#Test
public void test_Login_002(){
System.out.println("test_Login_002");
}
#Test
public void test_Login_003(){
System.out.println("test_Login_003");
}
}
public class Logout {
#Test
public void test_Logout_001(){
System.out.println("test_Logout_001");
}
#Test
public void test_Logout_002(){
System.out.println("test_Logout_002");
}
#Test
public void test_Logout_003(){
System.out.println("test_Logout_003");
}
}
From the above classes I want to execute test methods test_Login_001 , test_Login_003 , test_Logout_002 only.
How this can be achieved in junit4 ?
Since JUnit 4.8 introduced Categories there exists a clean solution, create a TestSuite:
#RunWith(Categories.class)
#IncludeCategory(MustHaveTests.class)
#SuiteClasses( { Test_Login.class, Test_Logout.class })
public class MustHaveTestsTestSuite {
public interface MustHaveTests { /* category marker */ }
}
And add the #Category(MustHaveTests.class) above every test you would like to run with the TestSuite, e.g.:
#Category(MustHaveTests.class)
#Test
public void test_Login_001(){
System.out.println("test_Login_001");
}
When running the TestSuite only the MustHaveTests-"tagged" tests will be executed. More Details on #Category: https://github.com/junit-team/junit4/wiki/categories
You need to create an org.junit.runner.Request and pass it to the JunitCore runner, or actually to any Runner.
JUnitCore junitRunner = new JUnitCore();
Request request = Request.method(Logout.class, "test_Logout_002");
Result result = junitRunner.run(request);
I actually created an Annotation and can search for methods with those annotations and dynamically create Request and run them
public class TestsSuite {
public static void main(String[] args) throws Exception {
Class annotation = MyTestAnnotation.class;
JUnitCore junitRunner = new JUnitCore();
Class testClass = Test_Login.class;
Method[] methods = testClass.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(annotation)) {
if (method.isAnnotationPresent(org.junit.Test.class)) {
Request request = Request.method(testClass, method.getName());
Result result = junitRunner.run(request);
System.out.println(result.wasSuccessful());
}
}
}
}
}
This might not be the slickest implementation, but I solved a similar problem by created a new #SuiteMethods annotation as follows:
SuiteMethods.java
#Retention(RUNTIME)
#Target(TYPE)
public #interface SuiteMethods {
String[] value() default {""};
}
FilteredSuite.java
public class FilteredSuite extends Categories {
private static String[] TEST_METHODS_TO_RUN = {""}; // default behavior is to run all methods
private static Class<?> extractMethodNamesFromAnnotation(Class<?> clazz) {
SuiteMethods methodsAnnotation = clazz.getAnnotation(SuiteMethods.class);
if (methodsAnnotation != null) {
// if our MethodsAnnotation was specified, use it's value as our methods filter
TEST_METHODS_TO_RUN = methodsAnnotation.value();
}
return clazz;
}
public static Filter getCustomFilter() {
Filter f = new Filter() {
#Override
public boolean shouldRun(Description desc) {
String methodName = desc.getMethodName();
for (String subString : TEST_METHODS_TO_RUN) {
if (methodName == null || methodName.contains(subString)) {
return true;
}
}
return false;
}
#Override
public String describe() {
return null;
}
};
return f;
}
public FilteredSuite(Class<?> arg0, RunnerBuilder arg1) throws InitializationError {
super(extractMethodNamesFromAnnotation(arg0), arg1);
}
#Override
public void filter(Filter arg0) throws NoTestsRemainException {
// At test suite startup, JUnit framework calls this method to install CategoryFilter.
// Throw away the given filter and install our own method name filter
super.filter(getCustomFilter());
}
}
A Usage Example
#RunWith(FilteredSuite.class)
#SuiteClasses({
GroupRestTest.class,
ScenarioRestTest.class
})
#SuiteMethods({
"testReadOnlyFlag",
"testSheetWriteData",
"testAddScenarioMeta"
})
public class SubsetTestSuite {
}
I have the following GWT module:
public class FizzModule implements EntryPoint {
private Buzz buzz;
public FizzModule() {
this(null);
}
public FizzModule(Buzz bz) {
super();
setBuzz(bz);
}
#Override
public void onModuleLoad() {
// ...etc.
}
}
I would like to "inject" FizzModule with a Buzz instance. However, all of the code examples I see for GWT modules do not use constructors. Instead, they bootstrap the DI mechanism (typically either ClientFactory or GIN) from inside the onModuleLoad() method. Is this something that GWT forces, or can I somehow inject my module before it loads to the client-side? Thanks in advance!
GWT instantiates your module using its zero-arg constructor, always.
(technically, I think it uses GWT.create() so you could use deferred binding rules, but that wouldn't change anything re. how its instantiated)
BTW, where would the Buzz instance come from?
You could add parameters to the URL and use PlaceController. Then get those values on module load.
public void onModuleLoad() {
SimplePanel mainPanel = new SimplePanel();
EventBus eventBus = GWT.creat(EventBus.class);
// Start ActivityManager for the main widget with ActivityMapper
ActivityManager activityManager = new ActivityManager(injector.getActivityMapper(),
eventBus);
activityManager.setDisplay(mainPanel);
RootPanel.get().add(mainPanel);
// Start PlaceHistoryHandler with our PlaceHistoryMapper
AppPlaceHistoryMapper contentHistoryMapper = GWT.create(AppPlaceHistoryMapper.class);
PlaceHistoryHandler historyHandler = new PlaceHistoryHandler(contentHistoryMapper);
PlaceController placeController = new PlaceController(eventBus)
historyHandler.register(placeController, injector.getEventBus(), new MainPlace());
// Goes to the place represented on URL else default place
historyHandler.handleCurrentHistory();
if(placeController.getWhere() instanceof MainPlace) {
(MainPlace).getFoo();
}
}
public class MainPlace extends Place {
private String foo;
public MainPlace(String token) {
String foo = token;
}
#Override
public String getFoo() {
return foo;
}
public static class Tokenizer implements PlaceTokenizer<MainPlace> {
#Override
public MainPlace getPlace(String token) {
return new MainPlace(token);
}
#Override
public String getToken(MainPlace place) {
return place.getFoo();
}
}
}