Weld using alternative producer from src/test/META

2019-06-04 04:34发布

问题:

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

回答1:

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>