I am trying to use Weld SE 2.3.0.Final to swap an alternative implementation of an injected dependency during testing by supplying a different beans.xml
in src/test/resources/META-INF
It always seems to use the main version of beans.xml
though and I'm not sure why.
First of all here are the supporting classes
Engine.java
public interface Engine
{
void start();
void stop();
}
DefaultEngine.java
@Vetoed
public class DefaultEngine implements Engine
{
public void start()
{
System.out.println("Cough cough vrummmmm");
}
public void stop()
{
System.out.println("Phhhut clank");
}
}
Car.java
public class Car
{
@Inject
private Engine engine;
public void startCar()
{
engine.start();
}
public void stopCar()
{
engine.stop();
}
}
EngineProducer.java
public class EngineProducer
{
@Produces
public Engine getEngine()
{
return new DefaultEngine();
}
}
App.java
public class App
{
@Inject
private Car car;
public void main(@Observes ContainerInitialized event)
{
car.startCar();
car.stopCar();
}
}
And the production version of beans.xml
in src/main/resources/META-INF
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/javaee/beans_1_0.xsd">
</beans>
The above code when run from App.java should print (and it does)
Cough cough vrummmmm
Phhhut clank
Now from the test code I want to use the SportsEngine
implementation. So I have the following
SportsEngine.java
@Vetoed
public class SportsEngine implements Engine
{
public void start()
{
System.out.println("Vrummm vrummm vrummmm!");
}
public void stop()
{
System.out.println("Prrrrrr clunk");
}
}
TestEngineProducer.java
@Alternative
public class TestEngineProducer
{
@Produces
public Engine getEngine()
{
return new SportsEngine();
}
}
AppTest.java
@RunWith(WeldJUnit4Runner.class)
public class AppTest
{
@Inject
private Car car;
@Test
public void testCar()
{
car.startCar();
car.stopCar();
}
}
WeldJUnit4Runner.java
public class WeldJUnit4Runner extends BlockJUnit4ClassRunner
{
/** The test class to run. */
private final Class<?> mKlass;
/** Weld infrastructure. */
private final Weld weld;
/** The container itself. */
private final WeldContainer container;
/**
* Runs the class passed as a parameter within the container.
*
* @param klass
* to run
* @throws InitializationError
* if anything goes wrong.
*/
public WeldJUnit4Runner(final Class<Object> klass) throws InitializationError
{
super(klass);
this.mKlass = klass;
this.weld = new Weld();
this.container = weld.initialize();
}
@Override
protected Object createTest() throws Exception
{
final Object test = container.instance().select(mKlass).get();
return test;
}
}
And the test version of beans.xml
in src/test/resources/META-INF
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/javaee/beans_1_0.xsd">
<alternatives>
<class>com.test.testing.TestEngineProducer</class>
</alternatives>
</beans>
When the test method is run it should print
Vrummm vrummm vrummmm!
Prrrrrr clunk
But instead it seems to use the EngineProducer
instead of the TestEngineProducer
and prints the output twice
Cough cough vrummmmm
Phhhut clank
Cough cough vrummmmm
Phhhut clank
Why is it not using the test beans.xml?
If I put the <alternatives>
section from test beans.xml and put it in the main beans.xml then it works, but I don't want to swap the beans.xml in main/resources/META-INF when I'm testing
It seems a bug of Weld Se. It only use beans.xml version under src/main/resources/META-INF.
The solution is using beans.xml under CDI-SE src/main/resources/META-INF/beans.xml for test purpose and you may need to add < exclude> into beans.xml too.
You can have another production beans.xml file and when you export your code into deployment package (jar, war, ear). Don't forget to replace your testing beans.xml by your production beans.xml.
If you are developing web app. You can create your production beans.xml under WEB-INF. 2 version of beans.xml exists but only WEB-INF/beans.xml is used by WAS.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
version="1.1" bean-discovery-mode="all">
<scan>
<exclude name="org.jboss.weld.**" />
<exclude name="com.your_package.EngineProducer" />
</scan>
<alternatives>
<class>com.test.testing.TestEngineProducer</class>
</alternatives>
</beans>