How to do CDI / SE from Spock Test?
I use CDIUnit and other various forms of CDI / SE solutions to implement tests (junit) that do less mockery and use more of my application stack (integration tests I suppose).
Anyway, I also really like Spock for testing, and thought it should be tied to linking something from the CDI / SE and spock space to be able to create specs that have access to most of the CDI stuff.
Many of the runners for CDI (CDIUnit, weldjunit, deltaspike, random blog snippets that do cdi or weld for SE) are JUnit Runners and don't seem to be very good at Spock. After a lot of rework, I went overboard with a very simple thing: weld / se + spock with a really simple @Inject. But that won't work.
The weld seems to start to "fire up" and my injection message constructor fires. However, the injection formula reference is zero in the Spock function method. In the next test, I was using the @Inject setter, not an instance variable, and the setter seemed to trigger a non-null reference, but by the time my function method was run; the link is null again. I also messed up with Weld and @Shared container creation and tried to initialize the lifecycle setupSpec () method; the same results.
Can simple CDI stuff be done from Spock test?
Here's a hunk Spock example that shows what I'm trying to achieve:
package fhw
import spock.lang.Specification
import javax.inject.Inject
import org.jboss.weld.environment.se.Weld
import org.jboss.weld.environment.se.WeldContainer
import spock.lang.Shared
public class Chompers
extends Specification
{
def Weld weld
def WeldContainer container
def setup()
{
println this
weld = new Weld()
container = weld.initialize()
container.instance().select(this.class).get()
//container.instance().select(this.class)
}
@Inject
def SomethingToTest somethingToTest;
//@Inject
//def setSomethingToTest(SomethingToTest sst)
//{
// somethingToTest = sst
// println "setter fired and somethingToTest is null? " + (null == somethingToTest)
//}
def "my first Test"()
{
given:
println "given I used weld wrapper spec From spock (instance " + this + ")"
when:
println "when I do something, in this case, like just haveing @Inject annotation"
then:
somethingToTest
}
}
UPDATE
I have something like "working". Not sure why or how and not sure if it's even good (for a solution). I would like to know more about what is happening and why. Examples:
package fhw
import org.junit.runner.RunWith
import spock.lang.*
import org.jboss.weld.environment.se.Weld
import org.jboss.weld.environment.se.WeldContainer
import javax.enterprise.inject.Instance
class WeldSpec
extends Specification
{
def Weld weld
def WeldContainer container
def me
def setup()
{
weld = new Weld()
container = weld.initialize()
me = container.instance().select(this.class).get()
}
def cleanup()
{
if(weld)
{
weld.shutdown()
}
}
}
package fhw
import spock.lang.*
import javax.inject.Inject
import fhw.spock.*
public class Bonnie
extends WeldSpec
{
@Inject
def SomethingToTest somethingToTest;
def "my silly first cdi-spock test"()
{
when:
def s = me.somethingToTest.upShift("fred")
then:
"FRED" == s
}
}
package fhw;
import javax.annotation.PostConstruct;
import javax.inject.Named;
@Named
public class SomethingToTest
{
public SomethingToTest() {}
@PostConstruct
private void init()
{
System.out.println("SomethingToTest: post construction");
}
public String upShift(String in)
{
String s = null;
if(null != in)
{
s = in.toUpperCase();
}
return(s);
}
}
UPDATE 2
So what I noticed / assumed that what actually happened was that spock instantiated my spec ('this') and then in setup using weld / se I created another one with select () calls. get () ('me'). Plain printlns showed 'me'! = 'This', and indeed the injection happened on a managed instance created with select (). Get (). Or I think.
So, I think I really want the injection to happen on 'this'. Some googling and I came across this: CDI injection into an existing object , something similar. I became inspired by DeltaSpike injectFields and refactored WeldSpec as:
package fhw
import spock.lang.*
import javax.enterprise.inject.spi.BeanManager
import javax.enterprise.inject.spi.InjectionTarget
import org.jboss.weld.environment.se.Weld
import org.jboss.weld.environment.se.WeldContainer
import javax.enterprise.context.spi.CreationalContext
import javax.enterprise.inject.spi.AnnotatedType
class WeldSpec
extends Specification
{
def Weld weld
def WeldContainer container
def setup()
{
weld = new Weld()
container = weld.initialize()
BeanManager beanManager = container.getBeanManager()
CreationalContext<? extends WeldSpec> creationalContext = beanManager.createCreationalContext(null)
AnnotatedType<? extends WeldSpec> annotatedType = beanManager.createAnnotatedType((Class<? extends WeldSpec>) this.getClass())
InjectionTarget<? extends WeldSpec> injectionTarget = beanManager.createInjectionTarget(annotatedType)
injectionTarget.inject(this, creationalContext);
}
def cleanup()
{
if(weld)
{
weld.shutdown()
}
}
}
This works, and my spec is somewhat more natural:
package fhw
import spock.lang.*
import javax.inject.Inject
import fhw.spock.*
public class Bonnie
extends WeldSpec
{
@Inject
def SomethingToTest somethingToTest;
def "my silly first cdi-spock test"()
{
when:
def s = somethingToTest.upShift("fred")
then:
"FRED" == s
}
}
Now to see if this is good for anything ....
source to share
Update2 has a performance issue: because setup () is run before each method, Weld is initialized for every method on the objects - with the scan classpath and all.
I moved it to static - but there is no shutdown in this case (I couldn't find a place for it outside of the outriggers, but it probably isn't even needed for tests):
class CdiSpecification extends Specification {
private static Weld weld
private static WeldContainer container
private static BeanManager beanManager
static {
weld = new Weld()
container = weld.initialize()
beanManager = container.getBeanManager()
}
def setup() {
CreationalContext<? extends CdiSpecification> creationalContext = beanManager.createCreationalContext(null)
AnnotatedType<? extends CdiSpecification> annotatedType = beanManager.createAnnotatedType((Class<? extends CdiSpecification>) this.getClass())
InjectionTarget<? extends CdiSpecification> injectionTarget = beanManager.createInjectionTarget(annotatedType)
injectionTarget.inject(this, creationalContext);
}
// def cleanup() {
// if (weld) {
// weld.shutdown()
// }
// }
}
source to share