Quarkus: java.lang.IllegalStateException: This method is normally automatically..... in IT tests

Created on 28 Mar 2019  Â·  43Comments  Â·  Source: quarkusio/quarkus

I got this Exception running IT tests with 'mvn test':

java.lang.IllegalStateException: This method is normally automatically overridden in subclasses: did you forget to annotated your entity with @Entity?

this happen when I call this simple method of OrdiniService injected in the test:

@Path("/ordini")
@Slf4j
@Validating
@Authorizing
@Dependent
public class OrdiniService implements PanacheRepositoryBase<Ordine, String> {
    @GET
    @NoCache
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Ordine findOrdineById(@PathParam("id") @NotNull String id) {

        return findById(id);
    }

All works as expected when I run this method through the JAX-RS interface with the 'quarkus:dev' goal.

arepanache kinbug pinned

Most helpful comment

I found an interesting, somehow clean workaround:

@QuarkusTest
class UserRepositoryTest {
    @Inject
    UserRepositoryWrapper userRepository;

    @Test
    void persist() {
        final User user = User.builder()
                              .firstName("Naruto")
                              .lastName("Uzumaki")
                              .age(10)
                              .build();

        userRepository.persist(user);

        assertNotNull(user.getId());
    }

    @Test
    void listAll() {
        final List<User> users = userRepository.listAll();
        assertFalse(users.isEmpty());
    }

    @Test
    void name() {
        final User user = userRepository.find("firstName", "Naruto").firstResult();
        assertNotNull(user);
    }

    /**
     * Workaround to fix https://github.com/quarkusio/quarkus/issues/1724.
     */
    @ApplicationScoped
    @Transactional
    static class UserRepositoryWrapper {
        @Inject
        @Delegate
        UserRepository userRepository;
    }
}

This is using lombok @Delegate so you call the methods directly from the Wrapper. Once this is fixed, only the type needs to be changed. Alternative, a get method to the UserRepository on the inner class can be exposed and it also seems to work.

All 43 comments

This happens to work in my test. Could you should me your test class?

Given that OrdiniService implements PanacheRepositoryBase,
I can reproduce with this method (using 0.12.0 and master):

@QuarkusTest
public class QuarkusResourceTest extends AbstractTest {

@Inject
OrdiniService ordiniService;

@Test()
public void testFindById() {

    assertThrows(java.lang.IllegalStateException.class, () -> {
        ordiniService.findById("existing order");
    });
}

Il giorno ven 29 mar 2019 alle ore 17:56 Stéphane Épardaud <
[email protected]> ha scritto:

This happens to work in my test. Could you should me your test class?

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/quarkusio/quarkus/issues/1724#issuecomment-478072536,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AB9yRi_XeNosxoUKUQKkaBnIcAsabdsMks5vbkWogaJpZM4cPmeH
.

--


http://www.lucamasini.net
http://twitter.com/lmasini
http://www.linkedin.com/pub/luca-masini/7/10/2b9


OK thanks. I can reproduce now. It turns out that this is due to the test classloader using OrdiniService from target/classes (unaugmented) instead of target/test-classes (augmented) as soon as I try to inject this service in the test. Removing the injection makes the right service be used.

OK I got it: what happens is that JUnit loads the OrdiniService class in its classloader (which as test-classes (augmented) and classes (non-augmented) _before_ we generate the augmented classes. And once they are loaded it's too late.

@stuartwdouglas or @dmlloyd didn't we already have an issue related to junit class loading in the past? Any idea what we can do here?

+1

@stuartwdouglas or @dmlloyd any opinion what we could do here? Several people have hit this issue and I'm really not sure what to do about it.

We could rewrite the whole thing to do augmentation well before the test phase... other than that I don't know.

I'd be fine with that.

I've updated the test to represent this issue
https://github.com/danielpetisme/quarkus/blob/fix/1724/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/PanacheFunctionalityTest.java#L55
(All PanacheFunctionalityTest tests fail now)

From what I could see (I will focus on JUnit5 only)
When we reach:
https://github.com/quarkusio/quarkus/blob/master/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java#L278
JUnit already loaded classes from target/classes (not augmented).

then we start Quarkus to take classes from target/classes augment them and write a the output to target/test-classes
https://github.com/quarkusio/quarkus/blob/master/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java#L70

Finally we create the test
https://github.com/quarkusio/quarkus/blob/master/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java#L331

After digging, in
https://github.com/quarkusio/quarkus/blob/master/test-framework/common/src/main/java/io/quarkus/test/common/TestInstantiator.java
The Arc container already registered a not augmented reference to the wanted class (here PersonRepository). That's sound weird since Arc is started with the app... that means Arc is fed with the JUnit classloader and not the with the app classloader containing the augmented classes

IMHO, there are 2 options:

  • I don't know when Arc is been fed with dependency resolutions but we could initialize it with the augmented classes or consider to "manually refresh" the references when the augmentation is done in order to have a proper dependency injection before TestInstantiator.instantiateTestis called. Looks like a "hack" but sounds achievable
  • Since the issue is JUnit loading classes before Quarkus being lunch. Could we bind the Quarkus augmentation to the test-compile Maven stage in order to have everything setup before JUnit is invoked. That means we don't have to "re-augment" the classes in the QuarkusTestExtension.

Is the analysis correct? WDYT? How can I move forward?

Since the issue is JUnit loading classes before Quarkus being lunch. Could we bind the Quarkus augmentation to the test-compile Maven stage in order to have everything setup before JUnit is invoked. That means we don't have to "re-augment" the classes in the QuarkusTestExtension.

This is what @dmlloyd suggested. We would need @stuartwdouglas 's opinion about this.

That won't help when running from an IDE.

I will need to look into this. JUnit 5 is actually really restrictive around this, in a lot of ways it is less extensible than JUnit 4.

I assume this JUnit 5 issue is relevant? https://github.com/junit-team/junit5/issues/201

Any news here?

I found an interesting, somehow clean workaround:

@QuarkusTest
class UserRepositoryTest {
    @Inject
    UserRepositoryWrapper userRepository;

    @Test
    void persist() {
        final User user = User.builder()
                              .firstName("Naruto")
                              .lastName("Uzumaki")
                              .age(10)
                              .build();

        userRepository.persist(user);

        assertNotNull(user.getId());
    }

    @Test
    void listAll() {
        final List<User> users = userRepository.listAll();
        assertFalse(users.isEmpty());
    }

    @Test
    void name() {
        final User user = userRepository.find("firstName", "Naruto").firstResult();
        assertNotNull(user);
    }

    /**
     * Workaround to fix https://github.com/quarkusio/quarkus/issues/1724.
     */
    @ApplicationScoped
    @Transactional
    static class UserRepositoryWrapper {
        @Inject
        @Delegate
        UserRepository userRepository;
    }
}

This is using lombok @Delegate so you call the methods directly from the Wrapper. Once this is fixed, only the type needs to be changed. Alternative, a get method to the UserRepository on the inner class can be exposed and it also seems to work.

Is this still an issue? I am hopeful that the change that added DeploymentClassLoader would have fixed this.

OK, lemme try it again, then.

Nope, sorry @stuartwdouglas the problem is still there :(

I can confirm the issue is still happening. Just tried it with Quarkus 0.26.1.

How can I reproduce? I tried to follow the steps above and everything passed for me.

Open PanacheFunctionalityTest and add this field: Person p = new Person();, now try to run the test and it will fail.

Good news and bad news.

The bad news is there is nothing I can do with the current approach to make this work.

The good news is that JUnit 5.5 added a hook that I should be able to use to work around this entirely, and fix a heap of the fragility here by going back to using RuntimeClassLoader.

Actually it is just bad news, it looks like even though they added org.junit.jupiter.api.extension.InvocationInterceptor, they also added some internal checks that deliberately prevent it being used for this use case.

So one option here would be to forbid the use of @BeforeAll and instead have a @QuarkusBeforeAll. Then I can use the same techniques as QuarkusUnitTest to proxy the invocations to a test instance I define in RuntimeClassLoader

So if JUnit implements https://github.com/junit-team/junit5/issues/2083 I can make this work transparently

I've just hit the same issue (Quarkus version 0.28.1). Basically I have a Book Panache entity:

@Entity
public class Book extends PanacheEntity {

  public String title;
  public String description;
  public Float price;
  ....
}

Manipulated by a transactional service :

@ApplicationScoped
@Transactional(SUPPORTS)
public class BookService {

  @Transactional(REQUIRED)
  public Book createBook(Book book) {
    Book.persist(book);
    return book;
  }

  public Book findBook(Long id) {
    return Book.findById(id);
  }
}

And the Quarkus test injects the service:

@QuarkusTest
public class BookServiceTest {

  @Inject
  private BookService bookService;

  @Test
  public void shouldCreateABook() throws Exception {

    // tag::shouldCreateABook[]
    Book book = new Book().title("Java EE 7").price(23.5F).isbn("1-84023-742-2").nbOfPages(354);

    book = bookService.createBook(book);
    assertNotNull(book.id, "Id should not be null");
  }
}

The test fails and Quarkus complains about the findById method (I do not have any @BeforeAll):

java.lang.IllegalStateException: This method is normally automatically overridden in subclasses: did you forget to annotate your entity with @Entity?

Yeah, don't declare your service as a field: look it up with the programmatic API. That's the workaround, sorry :(

@agoncal try to wrap your service into another (can use Lombok Delegate). It does work if the injection is indirect by another service.

Hi, somebody closed my previous issue:
https://github.com/quarkusio/quarkus/issues/2949

But it seems that it was duplicate of this one anyway, so I won't reopen a new one.

I have executed my test case against 1.0.0.CR1 and here are the results:


Test set: org.NoSuchMethodTest

Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.001 s <<< FAILURE! - in org.NoSuchMethodTest
testNoSuchMethodError Time elapsed: 0.001 s <<< ERROR!
java.lang.NoSuchMethodError: 'void org.redhat.Invoice.setAccountId(java.lang.String)'
at org.NoSuchMethodTest.testNoSuchMethodError(NoSuchMethodTest.java:38)

My test case is here:
https://github.com/agiertli/quarkus-nosuchmethod-error

Can somebody please look into it? This is opened since March this year and it's the reason why I had to abandon quarkus in my personal projects, and it's also the reason why I won't be able to recommend quarkus to customers.

So if JUnit implements junit-team/junit5#2083 I can make this work transparently

junit seem to have implemented this - can we fix this now then ?

Yes!

Well sort of, they have not released it yet, but it will be integrated into the new ClassLoader work at https://github.com/stuartwdouglas/quarkus/tree/new-class-loading . Even if the timing does not work we can just document how to work around the double @BeforeAll.

Yeah, don't declare your service as a field: look it up with the programmatic API. That's the workaround, sorry :(

In my scenario I had also to add to the application.properties:

quarkus.arc.remove-unused-beans=framework

Is this OK, for a clean(ish) workaround?
```
@QuarkusTest
public class QuarkusResourceTest extends AbstractTest {

    //If you remove @Inject, make sure property quarkus.arc.remove-unused-beans 
    //equals to false otherwise init() will fail.
    @Inject
    OrdiniService ordiniService;

    //Workaround for issue described in https://github.com/quarkusio/quarkus/issues/1724.
    @PostConstruct
    public void init() {
        ordiniService = CDI.current().select(OrdiniService.class).get();
    }

    @test()
    public void testFindById() {
        assertThrows(java.lang.IllegalStateException.class, () -> {
            ordiniService.findById("existing order");
        });
    }
}

@stuartwdouglas when that big classloader PR is merged this gets resolved too ?

yes

And there's a test for it, so let's close it.

I have followed @radcortez approach ( thank you very much ) but it only works ( I think ) with direct injections..... let me explain.

In those tests I have injected :

  • a service, that has injected repositories
  • 2 repositories

Wrapper approach seems to work with the repositories but not with the service's repositories.

 @QuarkusTest
public class ClinicServiceTests {
    @Inject
    ClinicServiceWrapper clinicService;

    @Inject
    PetTypeRepositoryWrapper petTypeRepositoryWrapper;    

    @Inject
    VetRepositoryWrapper vetRepositoryWrapper;

    @ApplicationScoped
    @Transactional
    static class PetTypeRepositoryWrapper {
        @Inject
        @Delegate
        JpaPetTypeRepository petTypeRepository;        
    }
    @ApplicationScoped
    @Transactional
    static class VetRepositoryWrapper {        
        @Inject
        @Delegate
        JpaVetRepository vetRepository;
    }    

    @ApplicationScoped
    @Transactional
    static class ClinicServiceWrapper {        
        @Inject
        @Delegate
        ClinicService clinicService;
    }

And the service

@ApplicationScoped
public class ClinicServiceImpl implements ClinicService {

    JpaPetRepository petRepository;
    JpaVetRepository vetRepository;
    JpaOwnerRepository ownerRepository;
    JpaVisitRepository visitRepository;
    JpaSpecialtyRepository specialtyRepository;
    JpaPetTypeRepository petTypeRepository;

     public ClinicServiceImpl(
             JpaPetRepository petRepository,
             JpaVetRepository vetRepository,
             JpaOwnerRepository ownerRepository,
             JpaVisitRepository visitRepository,
             JpaSpecialtyRepository specialtyRepository,
             JpaPetTypeRepository petTypeRepository) {
        this.petRepository = petRepository;
        this.vetRepository = vetRepository;
        this.ownerRepository = ownerRepository;
        this.visitRepository = visitRepository;
        this.specialtyRepository = specialtyRepository; 
        this.petTypeRepository = petTypeRepository;
    }

I'm facing the same issue (in 1.2.0.Final) and neither of the workarounds works :(

In my case I have AbstractUsecaseTest with all the tests and abstract SomeService getService() method in it.
Then I have ParticularUsecaseTest extends AbstractUsecaseTest where I only @Inject SomeService and return it in getService()

  • I don't want to change my abstract class to use a delegate (will break other tests).
  • Tried to @Inject a wrapper in the test that in turn @Inject the service but that didn't work either (service is null)
  • The @PostConstruct approach behaves exactly the same as direct @Inject (regardless of quarkus.arc.remove-unused-beans value)

@Milen , can you try your code with the local built of Quarkus at master (
999-SNAPSHOT) ? I havent tried deeply but I think it solved this issue for
me.

On Tue, Feb 4, 2020 at 9:11 PM Milen Dyankov notifications@github.com
wrote:

I'm facing the same issue (in 1.2.0.Final) and neither of the workarounds
works :(

In my case I have AbstractUsecaseTest with all the tests and abstract
SomeService getService() method in it.
Then I have ParticularUsecaseTest extends AbstractUsecaseTest where I
only @Inject SomeService and return it in getService()

  • I don't want to change my abstract class to use a delegate (will
    break other tests).
  • Tried to @Inject a wrapper that @Inject the service in the test but
    that didn't work either (service is null)
  • The @PostConstruct approach behaves exactly the same as direct
    @Inject (regardless of quarkus.arc.remove-unused-beans value)

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/quarkusio/quarkus/issues/1724?email_source=notifications&email_token=AAOALERP52R5RME62VONVY3RBHDXLA5CNFSM4HB6M6D2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEKZATZA#issuecomment-582093284,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAOALEUYPBNPIQX3MFS2TGTRBHDXLANCNFSM4HB6M6DQ
.

@jonathanvila I tried 999-SNAPSHOT but I can't get Quarkus to start with it :(
mvn clean compile quarkus:dev hangs after

...
[INFO] --- quarkus-maven-plugin:999-SNAPSHOT:dev (default-cli)  ---
Listening for transport dt_socket at address: 5005

UPDATE:

Oh I guess there is some timeout as after a long while it crashes with "Failed to create the application model" caused by "Failed to inject extension deployment dependencies". My app works just fine with 1.2.0.Final though.

Jajajajajaja man

Add the black magic to it : -DskipTests

El mié., 5 feb. 2020 14:45, Milen Dyankov notifications@github.com
escribió:

@jonathanvila https://github.com/jonathanvila I tried 999-SNAPSHOT but
I can't get Quarkus to stat with it :(
mvn clean compile quarkus:dev hangs after

...
[INFO] --- quarkus-maven-plugin:999-SNAPSHOT:dev (default-cli) ---
Listening for transport dt_socket at address: 5005

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/quarkusio/quarkus/issues/1724?email_source=notifications&email_token=AAOALEXFSP53G7EJ3C2ARZDRBK7HXA5CNFSM4HB6M6D2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEK3O6OA#issuecomment-582414136,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAOALEQFHXWVDD7JRR64IMLRBK7HXANCNFSM4HB6M6DQ
.

Ohhh sorry, I thought it was a problem on build time

Forget what I said about tests

I will. Check if mine behaves the same

El mié., 5 feb. 2020 15:09, Jonathan Vila Lopez jonathan.vila@gmail.com
escribió:

Jajajajajaja man

Add the black magic to it : -DskipTests

El mié., 5 feb. 2020 14:45, Milen Dyankov notifications@github.com
escribió:

@jonathanvila https://github.com/jonathanvila I tried 999-SNAPSHOT but
I can't get Quarkus to stat with it :(
mvn clean compile quarkus:dev hangs after

...
[INFO] --- quarkus-maven-plugin:999-SNAPSHOT:dev (default-cli) ---
Listening for transport dt_socket at address: 5005

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/quarkusio/quarkus/issues/1724?email_source=notifications&email_token=AAOALEXFSP53G7EJ3C2ARZDRBK7HXA5CNFSM4HB6M6D2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEK3O6OA#issuecomment-582414136,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAOALEQFHXWVDD7JRR64IMLRBK7HXANCNFSM4HB6M6DQ
.

Can you get a jstack thread dump to see what is going on?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

FroMage picture FroMage  Â·  75Comments

dirkweil picture dirkweil  Â·  79Comments

rdifrango picture rdifrango  Â·  62Comments

sberyozkin picture sberyozkin  Â·  51Comments

kny78 picture kny78  Â·  49Comments