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.
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.
Perhaps https://junit.org/junit5/docs/5.3.0/api/org/junit/jupiter/api/extension/TestInstanceFactory.html could get us some distance.
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:
TestInstantiator.instantiateTest
is called. Looks like a "hack" but sounds achievabletest-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:
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 :
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()
@Inject
a wrapper in the test that in turn @Inject
the service but that didn't work either (service is null
) @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?
Most helpful comment
I found an interesting, somehow clean workaround:
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.