Comments (12)
CC @manovotn
I think a sensible approach is to:
- Fix the inconsistency we have in that we should also take into consideration method params with
@InjectMock
when marking beans as unremovable - Add documentation that will mention that
@InjectMock
isn't a universal replacement for Mockito. It has a narrow use case for mocking missing beans.
The way JUnit works, you should be able to meld together several extensions and Mockito has their own one that you can leverage for what you are trying to do. What @mkouba suggests in the last comment is IMO the "cleanest" approach you can get.
from quarkus.
/cc @Ladicek (arc), @manovotn (arc), @mkouba (arc)
from quarkus.
As far as i understand QuarkusComponentTest is just 'mockito-test' with other annotations.
@octopus-prime It is not.
So QuarkusComponentTest still relies on Mockito.
Not really, you can use QuarkusComponentTest
even without Mockito and mocks in general.
The difference is that QuarkusComponentTest
starts a real CDI container plus configuration service. Mocks are automatically used if a component has an unsatisfied dependency; e.g. there's a component Foo
which also depends on Bar
. If you test Foo
in your QuarkusComponentTest
but no Bar
bean is available then a mock is injected instead.
Expected behavior
A fully initialized mockito - with parameter resolvers.
We don't use the MockitoExtension
at all. Therefore, the @Mock
parameters may not be resolved.
If you need to inject an unconfigured mock in a test method argument then replace the @Mock
with @InjectMock
.
from quarkus.
@QuarkusComponentTest
class ReadFallControllerTest {
@Inject
ReadFallController readFallController;
@InjectMock
FallRepository fallRepository;
@InjectMock
FallMapper fallMapper;
@Test
void readFall(@InjectMock Fall fall, @InjectMock FallResponse fallResponse) {
when(fallRepository.findByFallNummerOrThrow(TestData.FALLNUMMER)).thenReturn(fall);
when(fallMapper.map(fall)).thenReturn(fallResponse);
FallResponse result = readFallController.readFall(TestData.FALLNUMMER);
assertThat(result, is(fallResponse));
}
}
->
Failed to resolve parameter
No matching bean found for the type
from quarkus.
Failed to resolve parameter No matching bean found for the type
Could you share the full stacktrace pls?
Also it would be great if you could share a small project that would contain the "full" setup of your test.
from quarkus.
https://github.com/octopus-prime/qct
The 'BrokenFooControllerTest' will produce
Failed to resolve parameter [com.example.Foo arg0] in method [void com.example.BrokenFooControllerTest.getFoo(com.example.Foo,com.example.FooResponse)]: No matching bean found for the type [class com.example.Foo] and qualifiers []
org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter [com.example.Foo arg0] in method [void com.example.BrokenFooControllerTest.getFoo(com.example.Foo,com.example.FooResponse)]: No matching bean found for the type [class com.example.Foo] and qualifiers []
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: java.lang.IllegalStateException: No matching bean found for the type [class com.example.Foo] and qualifiers []
at io.quarkus.test.component.QuarkusComponentTestExtension.supportsParameter(QuarkusComponentTestExtension.java:300)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:178)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
from quarkus.
https://github.com/octopus-prime/qct
The 'BrokenFooControllerTest' will produce
Failed to resolve parameter [com.example.Foo arg0] in method [void com.example.BrokenFooControllerTest.getFoo(com.example.Foo,com.example.FooResponse)]: No matching bean found for the type [class com.example.Foo] and qualifiers [] org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter [com.example.Foo arg0] in method [void com.example.BrokenFooControllerTest.getFoo(com.example.Foo,com.example.FooResponse)]: No matching bean found for the type [class com.example.Foo] and qualifiers [] at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) Caused by: java.lang.IllegalStateException: No matching bean found for the type [class com.example.Foo] and qualifiers [] at io.quarkus.test.component.QuarkusComponentTestExtension.supportsParameter(QuarkusComponentTestExtension.java:300) at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:178) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
👍 I'll take a look tomorrow.
from quarkus.
@octopus-prime Ok, so the problem is that @InjectMock
is not intended for injection of a mock of any arbitrary object. It's meant to be used for beans; see the javadoc "Instructs the test engine to inject a mock instance of a bean into the annotated field or parameter.".
Now in the reproducer, Foo
and FooResponse
are not beans but more like DTOs. For these objects, @org.mockito.Mock
or Mockito#mock(Class)
are more appropriate, i.e. something like:
@Test
void getFoo() {
Foo foo = Mockito.mock(Foo.class);
FooResponse fooResponse = Mockito.mock(FooResponse.class);
when(fooRepository.findFoo("1")).thenReturn(foo);
when(fooMapper.mapFoo(foo)).thenReturn(fooResponse);
FooResponse result = fooController.getFoo("1");
assertEquals(result, fooResponse);
}
Again, for QuarkusComponentTest
it's not the goal to ease the creation of mocks but create mocked beans automatically if a component under the test (a CDI bean) has an unsatisfied dependency.
I wonder if we should change the behavior but I think that it does not make sense to create a mock bean for such an object. It seems like a misuse of the API. But I agree that we should at least improve the error message so that it's more clear. If we find a way to detect this kind of misuse...
CC @manovotn
from quarkus.
Strange... "@InjectMock is not intended for injection of a mock of any arbitrary object."... But
@QuarkusComponentTest
class UglyFooControllerTest {
@Inject
FooController fooController;
@InjectMock
FooRepository fooRepository;
@InjectMock
FooMapper fooMapper;
@InjectMock
Foo foo;
@InjectMock
FooResponse fooResponse;
@Test
void getFoo() {
when(fooRepository.findFoo("1")).thenReturn(foo);
when(fooMapper.mapFoo(foo)).thenReturn(fooResponse);
FooResponse result = fooController.getFoo("1");
assertEquals(fooResponse, result);
}
}
Works fine.
However... both solutions need extra-lines of code :-(
from quarkus.
Works fine.
Indeed, there's an inconsistency because @InjectMock
field does contribute to the logic that is used to exclude unused beans from removal and @InjectMock
parameter does not (a new bean is created for each @InjectMock
but then removed if not used in a tested component). We should unify this logic. But I'm not quite sure in which way. If we want to be more correct and resource efficient then @InjectMock
field should not work either. And we could make the @InjectMock
parameter work but it would still be a misuse and waste of resources in the sense that a new bean is created just to create an empty mock 🤷.
However... both solutions need extra-lines of code :-(
Yes, they need. There's one more alternative:
import org.mockito.Mock;
import io.quarkus.test.component.SkipInject;
@ExtendWith(MockitoExtension.class)
@SkipInject // needed to tell the QuarkusComponentTest to skip the @Mock params
@Test
void getFoo(@Mock Foo foo, @Mock FooResponse fooResponse) {
when(fooRepository.findFoo("1")).thenReturn(foo);
when(fooMapper.mapFoo(foo)).thenReturn(fooResponse);
FooResponse result = fooController.getFoo("1");
assertEquals(result, fooResponse);
}
from quarkus.
I think a sensible approach is to:
* Fix the inconsistency we have in that we should also take into consideration method params with `@InjectMock` when marking beans as unremovable * Add documentation that will mention that `@InjectMock` isn't a universal replacement for Mockito. It has a narrow use case for mocking missing beans.
We will also skip param injection for params annotated with @org.mockito.Mock
so that @SkipInject
is not needed.
from quarkus.
@octopus-prime pull request sent! ;-)
from quarkus.
Related Issues (20)
- Enum property values not hyphenated anymore in the configuration metadata HOT 12
- Version parsing fails with master GraalVM built with latest Labs JDK 24 ea HOT 3
- Quarkus is using Micrometer version that is reaching EOL HOT 1
- Enhancement : Improve documentation about using RestResponse vs Response (and parts related to reflection) HOT 1
- Update docs to reflect Panache annotation processor removed for Hibernate ORM/Hibernate Reactive/MongoDB HOT 1
- Integrate GraphQL clients with the TLS registry extension HOT 2
- REST usage fails with native when e.g. ContainerResponseFilter is used HOT 2
- Hibernate cache is using Caffeine instead of Infinispan HOT 4
- Since Keycloak 24 required action "verify profile" is enabled by default, preventing access token retrieval from dev service HOT 3
- org.eclipse.microprofile.config.spi.Converter: Provider io.quarkus.amazon.common.runtime.RegionConverter not found HOT 4
- Wrong callback URL in documentation for GitHub OIDC HOT 6
- Remove references to Renarde in https://quarkus.io/guides/security-openid-connect-providers HOT 3
- Add virtual threads support for all framework features including @Asynchronous & @Inject ExecutorService with Quarkus on JDK21 and later HOT 1
- CredentialsProvider is no longer calling the @PostConstruct init method prior to calling the getCredentials method HOT 1
- Listen on Unix Domain Socket passed via FD
- Deprecate `quarkus.datasource.jdbc.tracing` HOT 1
- be explicit/use examples in websocket.next docs when it comes to subscribe call for action
- CLI in version 3.8.5 does not properly update HOT 7
- quarkus update sometimes fails to correctly update @QuarkusTestResource HOT 1
- when/if onTextMessage throws exception connection is "quietly" closed without explanation
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from quarkus.