Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lost active contexts when using static weld #167

Closed
shishovsa opened this issue Jan 30, 2024 · 4 comments · Fixed by #168
Closed

Lost active contexts when using static weld #167

shishovsa opened this issue Jan 30, 2024 · 4 comments · Fixed by #168

Comments

@shishovsa
Copy link

When I using static weld object for creating WeldInitiator, active contexts was lost after two tests.

[ERROR] Errors: 
[ERROR]   WeldTest.doTest3:42 » ContextNotActive WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped
[ERROR]   WeldTest.doTest4:47 » ContextNotActive WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped

But when I using WeldInitiator.from(<classes>) all tests works fine.

The version of weld-junit is 2.0.2.Final, weld is 3.1.9 (Final), junit is 5.4.2.

@RequestScoped
class ServiceFoo {
    void run() {
    }
}

@ExtendWith(WeldJunit5Extension.class)
class WeldTest {

    @Inject
    protected ServiceFoo serviceFoo;

    static final Weld weld = WeldInitiator.createWeld();
    static {
        weld.addBeanClass(ServiceFoo.class);
    }

    @WeldSetup
    public WeldInitiator weldInitiator =
            WeldInitiator.from(weld)
            //WeldInitiator.from(ServiceFoo.class)
                    .activate(RequestScoped.class)
                    .build();

    @Test
    void doTest1() {
        serviceFoo.run();
    }

    @Test
    void doTest2() {
        serviceFoo.run();
    }

    @Test
    void doTest3() {
        serviceFoo.run();
    }

    @Test
    void doTest4() {
        serviceFoo.run();
    }
}
@manovotn
Copy link
Collaborator

Hello @shishovsa,

I took some time debugging this and below are my findings.

In your scenario you are using Weld-junit in a TestInstance.Lifecycle#PER_METHOD mode (which is junit's default) meaning that Weld will attempt to start/shutdown container for each test method.

    static final Weld weld = WeldInitiator.createWeld();
    static {
        weld.addBeanClass(ServiceFoo.class);
    }

The above lines result in a configured Weld builder stored statically. That means you are now carrying a configured builder instance between those test methods.

But you are also using a non-static setup to further configure Weld instance:

            WeldInitiator.from(weld)
                    .activate(RequestScoped.class)
                    .build();

Now, this code applies further configuration to Weld object and is invoked repeatedly for each test method again - reapplying whatever configuration you are adding. This causes issues because there's an underlying CDI extension responsible for context activations that ends up being registered with the same static Weld object repeatedly.

Maybe I am missing something but the setup you have seems rather weird - I recommend that you change it. You could do one of the following:

  • Make the WeldInitiator a static field which will result in a single configuration that's then reused for every test method
    • This helps because you will no longer try to re-apply part of your configuration on each method
  • Change test lifecycle to PER_CLASS so that you only start CDI container once
    • Note that this preserves CDI container state (including beans and their internal state) between test methods
  • Avoid using static Weld field and instead do the full configuration in the public @WeldSetup field

@manovotn
Copy link
Collaborator

I understand the underlying issue here is hard to spot for users so I've sent a PR that adds some documentation - #168

@shishovsa
Copy link
Author

Thank you @manovotn !

Maybe I am missing something but the setup you have seems rather weird...

Basically idea was to create abstract test class with all setup: weld, mocks, dockers.
Then extend simple test classes with only test logic (integration tests).

To prevent unwanted long-run initialization all services was possibly static and PER_CLASS.
In @AfterEach-method system was clearing.

My trouble is that WeldInitiator was not static field.
Alternatively case with full configuration in the @WeldSetup also works fine.
And maybe it is better to do Weld not static and use PER_METHOD to reinitialize request scoped beans from test to test...

Thank you again!

A bit complex example

public class SettingsManager {
    public String getConnectionString(){
        return "";
    }
}

@RequestScoped
class ServiceFoo {
    @Inject
    private SettingsManager settingsManager;

    void run() {
        System.out.println(settingsManager.getConnectionString());
    }
}

@ExtendWith(WeldJunit5Extension.class)
abstract class AbstractWeld {

    private static final SettingsManager settingsManager = Mockito.mock(SettingsManager.class);

    @Inject
    protected ServiceFoo serviceFoo;

    static {
        // docker start
        String dockerConnectionString = "str";

        // create some mocks
        Mockito.when(settingsManager.getConnectionString()).thenReturn(dockerConnectionString);
    }

    @WeldSetup
    public WeldInitiator weldInitiator =
            WeldInitiator.from(ServiceFoo.class)
                    .activate(RequestScoped.class)
                    .addBeans(MockBean.builder()
                            .types(SettingsManager.class)
                            .creating(settingsManager)
                            .build())
                    .build();

    @AfterEach
    void afterEach() {
        System.out.println("truncate docker database");
    }
}

class ITTestCase1 extends AbstractWeld {

    @Test
    void doTest1() {
        serviceFoo.run();
    }

    @Test
    void doTest2() {
        serviceFoo.run();
    }
}

class ITTestCase2 extends AbstractWeld {

    @Test
    void doTest1() {
        serviceFoo.run();
    }

    @Test
    void doTest2() {
        serviceFoo.run();
    }
}

@manovotn
Copy link
Collaborator

Basically idea was to create abstract test class with all setup: weld, mocks, dockers.
Then extend simple test classes with only test logic (integration tests).

That should work fine, Weld-junit will look for a singular @WeldSetupconfiguration in the class hierarchy and use that.
https://github.com/weld/weld-testing/tree/master/junit5#inheritance-of-test-classes

My trouble is that WeldInitiator was not static field.

It's perfectly fine to have WeldInitiator in a static field.

And maybe it is better to do Weld not static and use PER_METHOD to reinitialize request scoped beans from test to test...

That really depends on the contents of your test and the complexity to start them. Weld-junit is designed to start a minimal container so on its own it's taking very little time to boot and shouldn't cause you trouble.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants