-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Inject mock with Generics #15186
Comments
I think here it is the same issue. The idea was to have a GenericPanacheRepository. This does not work:
But removing generics, yes it is:
So what I noticed:
|
If I wanted to show some code snippets. The interface that is using the generic is:
The class that implements that interface, and I do note that I have 2 class implementing it hence the "Named" annotation is:
Finally I am trying to mock this service as follows:
|
@Vrael your problem is different. You are misusing DI. https://stackoverflow.com/questions/19234733/why-is-it-impossible-to-inject-generic-classes @myassine2 you might as well inject the implementation instead of the interface in that case as it is the same thing. Btw, if you have an interface that is implemented multiple times, it might be too generic and not focused on what you need your application to do (you are testing the implementation instead of the interface). In your example you are mocking a specific service and you will probably need to mock findAll method. If instead you had a FindPersons interface with findPersons() method, you could inject and mock that specific method and not care about how it was implemented (you could still implement that PersonService in the background, or not... who cares :D). But I guess this is beyond the scope of this issue. |
I tried mocking the implementation as you stated and it worked (Thank you!)! I saw that in debugging mode that it was using the mock instance. Unfortunately, when I tried to override the add method using Mockito as such:
I got an error, and in debug mode when I placed a breaking point inside the chain when I call it as such:
Perhaps Mockito is not mocking the default method? |
@myassine2 What I am saying is that you can replace every Named annotated field with its implementation as it is smelly... at least for me - it uses strings for matching and focuses too much on implementation anyway - if you say you want employee-person than you want that implementation anyway, so why would you inject an interface? (I'm not bashing your code, I just don't understand the purpose of that annotation in the first place). Also, having classes named *Impl seems off, as it really tells you nothing about the implementation. If it is just a regular service maybe it shouldn't be an interface in the first place because the interface will be basically the same as the implementation and will guide the structure of your code. Again, it's not you who is at fault here, but the IDEs force this convention and it is not helpful for anyone. Edit: Ignore the second paragraph. I forgot that you had a generic interface. I would still remove Impl as it adds nothing meaningful, but that is just me. |
For the use of the named annotation, I wanted to keep on using the interface so I may have several implementations for different data coming. In other words, I wanted to remove duplicate codes and have an abstraction layer. I found the silly issue from my side. I was using none reactive RestEasy package instead of the reactive one. For the using the named annotation, it was the only way to specify which implantation to use for the bean if I have multiple implantation. As for the naming *Impl, I saw it as a way to distinguish between the interface name and its implementations instead of both having the same name. On the contrary, it just directs to the implementation of that interface. So to finalize, the final Quarkus unit test class will look like the following
As @blazmrak stated (Again thank you so much for your support!), we should mock the implementation instead of the actual interface, and since the service already injects this implementation, it will inject this mock instead. |
@myassine2 you should not inject implementation in the tests or in code (be it via the actual class or the named annotation), because it will tie your code to the implementation and when the implementation will change your code and your tests will explode and you will have to fix all of them, which will deter you from restructuring and changing your code (at least you will not write the tests anymore, because they are useless). I would recommend you read Growing Object-Oriented Software Guided by Tests and see how the authors write interfaces and tests to be maintainable. |
Thank you for your reply and clarification! Again the service is using the interface with the implementation which is already calling the abstract methods. So I will only be mocking those methods which are in the interface which should be safe. Nonetheless, I will keep what you said in mind. As for the book, I was looking for a TDD book to read because I did not like "TDD by example", I couldn't grasp the concepts. I will definitely read "Growing Object-Oriented Software Guided by Tests" because I was looking for way to adapt TDD. |
Describe the bug
I am trying to implement unit tests for my application that uses generics. When I try to annotate the service with @InjectMock for a generic, I get an error that it is unable to detect the bean. I have even tried to add @nAmed to the inject mock to specify the bean to use, but to no avail.
Expected behavior
I should be able to mock classes with generics.
Actual behavior
It throws an error because it is unable to create a mock bean.
Caused by: java.lang.IllegalStateException: Invalid use of io.quarkus.test.junit.mockito.InjectMock - could not determine bean of type: interface org.acme.mongodb.panache.service.PersonService. Offending field is personEmployeeService of test class class org.acme.mongodb.panache.PersonEmployeeResourceTest
at io.quarkus.test.junit.mockito.internal.CreateMockitoMocksCallback.getBeanInstance(CreateMockitoMocksCallback.java:61)
at io.quarkus.test.junit.mockito.internal.CreateMockitoMocksCallback.afterConstruct(CreateMockitoMocksCallback.java:27)
... 66 more
To Reproduce
mongodb-panache-quickstart.zip
Steps to reproduce the behavior:
Configuration
Screenshots
Environment (please complete the following information):
uname -a
orver
: Linux Dev2servme-ThinkPad-L15-Gen-1 5.8.0-43-generic Color log support #49-Ubuntu SMP Fri Feb 5 03:01:28 UTC 2021 x86_64 x86_64 x86_64 GNU/Linuxjava -version
: openjdk version "11.0.10" 2021-01-19mvnw --version
orgradlew --version
): Apache Maven 3.6.3The text was updated successfully, but these errors were encountered: