Closed Pr3roxDLC closed 8 months ago
Hi @Pr3roxDLC
How does the bean you are trying to lookup looks like? If I were to take a guess, I suppose it has some CDI qualifier, right?
I think I know what the issue might be here - the test instance is not a CDI bean and we just treat it as injection target.
And it seems that CDI will detect the field injection and automatically add @Default
to it since there is no other qualifier present (which is correct for any other injection point).
I am not sure if this is fixable in weld-testing, I suppose not. We might be able to tackle in it Weld itself tho. I need to double check TCKs and weld tests to make sure this isn't intentional.
Meanwhile, as a workaround, I you could use one of these two:
1) Inject the instance as @Inject @Any Instance<X>
2) Obtain instance via CDI.current()
or @Inject BeanManager bm
and then bm.createInstance()
Hi @manovotn thanks for the fast response, the beans im trying to inject do (to my best knowledge) not have any qualifiers. They are simple classes without any scope or any other cdi related annotations, as we want to generate a new instance everytime instance.get() is invoked. We have already tried adding @Any to the injected field but this does not fix it. I will try the second workaround and let you know if it works.
Injected a BeanManager instance and then used createInstance() and it also fails with the following log
org.jboss.weld.exceptions.UnsatisfiedResolutionException: WELD-001334: Unsatisfied dependencies for type SomeClass with qualifiers
Note that now its missing all qualifiers
@Pr3roxDLC then this is something different than I thought, I am going to need a reproducer. You can use some of the existing tests (such as this one) and tweak it in your branch to showcase the problem.
Oh, another thing coming to my mind, is there a chance the synthetic bean archive is lacking that bean altogether? I.e. that it's not added into the archive.
I think if you just inject it via Instance
we cannot really detect the type and automatically add it.
Try adding this annotation to your test class - @AddBeanClasses(Transformer.class)
(or whatever is the name of the impl class you want to resolve)
@Pr3roxDLC then this is something different than I thought, I am going to need a reproducer. You can use some of the existing tests (such as this one) and tweak it in your branch to showcase the problem.
@manovotn i adjusted the test to represent my case as close as possible, i think for me the instance doesnt get directly injected into the test class but into a bean that then gets injected into the test but this fails in the exact same way, so i dont think it makes any difference.
Oh, another thing coming to my mind, is there a chance the synthetic bean archive is lacking that bean altogether? I.e. that it's not added into the archive. I think if you just inject it via
Instance
we cannot really detect the type and automatically add it.Try adding this annotation to your test class -
@AddBeanClasses(Transformer.class)
(or whatever is the name of the impl class you want to resolve)
Oh yes perfect, this actually fixes it
Oh, another thing coming to my mind, is there a chance the synthetic bean archive is lacking that bean altogether? I.e. that it's not added into the archive. I think if you just inject it via
Instance
we cannot really detect the type and automatically add it. Try adding this annotation to your test class -@AddBeanClasses(Transformer.class)
(or whatever is the name of the impl class you want to resolve)Oh yes perfect, this actually fixes it
Okay, actually using @AddBeanClasses({ -All of the classes that i want to ever inject- }) is what does the trick, is there a better way of doing this than listing all of them, i would guess that @AddPackages would be the way to do this? I was guessing that @AddPackages(value = AddPackages.All.class, recursively = true)
would fix the issue but im guessing my understanding on how this works is wrong
Right, so by default Weld SE isn't doing classic discovery (this can be achieved but kind of defeats the purpose of unit testing) and the annotation approach will not pick up classes from this other package as you aren't referencing them directly.
@AddPackages
should be a way to go but I presume that's not happening because in your case those classes have no bean defining annotations.
Starting from CDI 4.0, the default discovery mode is annotated
so Weld will scan that package but only process classes with bean defining annotations
Okay, actually using @AddBeanClasses({ -All of the classes that i want to ever inject- }) is what does the trick
Hm, I guess this way we explicitly register those classes and they get dependent scope. I'll have to check that tomorrow.
I did some debugging to confirm my assumptions and I do believe that the behavior here is a misconfiguration rather than a bug. But feel free to oppose me if you have an idea about how else you'd expect it to operate.
@AddPackages
is basically akin to doing Weld.addPackage()
in Weld SE - it IMO makes sense that this respects the discovery mode and only adds beans with bean defining annotations if you are running annotated
discovery mode. After all, you might be adding a package where not all classes are meant to be beans.
@AddBeanClasses
delegates directly to Weld.addBeanClass()
which means the class in question is directly registered with Weld as a bean class regardless of presence of a bean defining annotation. I find this sensible because you are explicitly mentioning the class - if it wasn't meant to be a bean, you'd simply not register it.
Now, I earlier mentioned you could change the discovery mode in Weld SE (which is what lies underneath weld-testing) - just to provide complete information, you'd need to do this via the WeldInitiator
approach instead of the annotation one (they cannot be combined, use one or the other)). That gives you full control and you can pass in whole Weld
instance meaning you get access to its configuration. You could use this to change the discovery mode inside the synthetic archive you are building - meaning the added package would then be handled as you expected.
@Pr3roxDLC am I making sense? Thoughts? :)
Hey @manovotn, i have been able to fix the issue by using
@WeldSetup
WeldInitiator weldInitiator = WeldInitiator.from(WeldInitiator.createWeld()
.addBeanClasses(getClasses())
.setBeanDiscoveryMode(BeanDiscoveryMode.ANNOTATED)
).activate(RequestScoped.class)
.build();
where getClasses() returns a list of all classes in our packages (collecting them using google guava), this is works for me. So i am guessing that this actually was a configuration mistake on my end, still confused on why the behaviour is different between tests and regular code tho. Maybe the underlying tomcat server changes some weld settings and this is what allows my code to work outside of tests.
still confused on why the behaviour is different between tests and regular code tho. Maybe the underlying tomcat server changes some weld settings and this is what allows my code to work outside of tests.
Tomcat won't be using Weld SE and there will be bean discovery whereas in weld-testing you don't do classic discovery and instead construct a synthetic archive to keep it minimal.
However, the fact that those classes get discovered in Tomcat and aren't added here when you use @AddPackages
seems weird.
What version of Weld are you using in your regular app and what version is used in weld-testing?
What version of Weld are you using in your regular app and what version is used in weld-testing?
Im believe that we are using weld 5.1.0,
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-core</artifactId>
<version>5.1.0.Final</version>
</dependency>
if this is what you mean by weld version, im not sure if the actual weld version is not actually set provided by the tomcat server (if so i wouldnt know how i can figure out the weld version)
and im using
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-junit5</artifactId>
<version>4.0.2.Final</version>
<scope>test</scope>
</dependency>
for the tests.
Hope this was the info you were looking for
if this is what you mean by weld version, im not sure if the actual weld version is not actually set provided by the tomcat server (if so i wouldnt know how i can figure out the weld version)
I don't think Tomcat as such includes Weld by default?
You are likely consuming weld servlet artifact (weld-servlet-shaded
or weld-servlet-core
).
If your project uses maven, then running mvn dependency:tree
might show you what you use and where it's coming from.
The reason I am asking is because from the fact that your app does discovers unannotated classes as beans can mean you are running older version of Weld/CDI (prior to 4.0 default discovery mode was all
).
Alternatively, your app might be using beans.xml
where you explicitly set the discovery mode to all
.
I can think of no other explanation of why it would behave differently in testing and "live" environment :shrug:
oh yeah okay i just checked and we do have a beans.xml
setup that sets the discovery mode to all, that seems to have been what caused the difference
oh yeah okay i just checked and we do have a
beans.xml
setup that sets the discovery mode to all, that seems to have been what caused the difference
I see, glad we identified the cause in the end!
You can configure weld-junit to also use all
discovery mode if you like although I would advocate for minimal deployments with annotated
mode even in the actual app :)
Otherwise, you just have to be conscious of this difference between the two environments when writing tests.
Closing the issue; feel free to reopen or simply keep commenting if it's still unclear.
This is my current Test Setup:
and
this fails for me with the following log:
It fails at the line shown above where we get an instance of a class that extends Transformer, this works perfectly outside of my tests and im wondering if i am doing something wrong here, if this is a technical limitation of this lib or if this is some kind of bug that can be resolved by you guys?