-
Notifications
You must be signed in to change notification settings - Fork 2k
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
@CustomTestApplication value cannot be annotated with @HiltAndroidApp #2033
Comments
I think there is some room for improvement in the documentation, but the suggested approach here is to move your
Your prod app now just extends the new base:
and your custom test app should too:
you'll also have to updates usages of your app, instead of casting the app context or app to your production app class cast it to the base class. |
Unfortunately that results in:
|
Ah, my bad, I forgot about that check. :( Added on ecd9e8f, you'll find the reason in the commit message. Due to injection timing we opted for banning injected fields in the test app instead of letting users run into NPEs due to injection not occurring in the App's There is probably a few paths you can take but if your intent is to scope and make
|
Yes, that works. I ended up putting this code outside the application, but otherwise it works fine. I think this is major enough to deserve going in the documentation, because it's kind of a tripping hazard 😄 |
Bad Solution
But it gives me exception:
So i'm directly passing the generated hilt application class for my test app and skip using |
@danysantiago I got the following issue when I use a base app. Do I missing something else ? java.lang.RuntimeException: Unable to instantiate application com.sample.MainApplication: java.lang.InstantiationException: java.lang.Class<com.sample.di.TestApplication> cannot be instantiated
|
Is there any possible way to execute instrumented tests with the following structure using hilt 2.32-alpha? I am able to run instrumented tests based on Any clues how to get injection wiht hilt inside Example:
|
Hi @hgross, We're currently working on a feature to allow entry points in such a case (with some caveats); however, it's still important to understand why this doesn't work by default in Hilt so that you can decide if this is something you really want to do. First, if you have to reuse the In addition, try to avoid entry point calls in the application. While calling entry points lazily, as in #2033 (comment) sometimes works, it would better to provide it via a module instead, if possible. That said, we do understand that there are some cases that required calling entry points in |
@bcorso are you referring to EarlyEntryPoints?
What about the scenario where you need to |
@mhernand40, for now we still don't allow using However, you can create an
While you could even just create an |
Thanks for the detailed answer and valuable testing hints @bcorso . One of the use cases @mhernand40 mentioned does apply to my requirements. I want to register a HILT-injected component to the lifecycle callbacks in the application class. This component is repsonsible to orchestrate initialization and teardown/shutdown. Regarding the |
Thanks for the suggestion @bcorso! Replacing |
Although @EarlyEntryPoint works for injecting into the Application#onCreate, the same dependency if is injected to another Android component such as a Service seems produce two instances of such dependency (even though annotated with @singleton). In my case I have something like this:
then in my BaseApplication I have this:
Then in one of my service I have:
The result is that the foo in my Service is different than the foo in my BaseApplication. |
@namgk, that's correct. The However, you may be able to avoid this issue if it's possible for you to create @EntryPoint
@InstallIn(SingletonComponent.class)
interface HiltEntryPoint {
Foo getFoo();
}
private Foo foo() {
// Create this lazily rather than in onCreate to avoid needing an @EarlyEntryPoint
return EntryPoints.get(this, HiltEntryPoint.class).getFoo();
} |
Thanks Brad,
Works for me. Interesting things to learn along the way :)
I think the way hilt works with tests isn't always intuitive. Like one
instance of application but different instance of SingletonComponent.
Where or when, or how such SingletonComponents are created by the way, if
not tied to Application#onCreate?
…On Thu, May 6, 2021 at 8:37 PM Brad Corso ***@***.***> wrote:
@namgk <https://github.com/namgk>, that's correct. The @Inject Foo field
does not come from the EarlyEntryPoint component. If you want the early
entry point Foo in your service you would have to also get it with an
early entry point EarlyEntryPoints.get(this,
HiltEntryPoint.class).getFoo().
However, you may be able to avoid this issue if it's possible for you to
create Foo lazily using a normal entry point, like:
@entrypoint
@Installin(SingletonComponent.class)
interface HiltEntryPoint {
Foo getFoo();
}
private Foo foo() {
// Create this lazily rather than in onCreate to avoid needing an @EarlyEntryPoint
return EntryPoints.get(this, HiltEntryPoint.class).getFoo();
}
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2033 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAHU7IEBZ5332ZHJ7Y5P7QTTMNN7FANCNFSM4PZJJ64Q>
.
|
A lot of the complexity around testing is due to how Gradle runs instrumentation tests. Gradle will run all tests using a single instance of an Application. This means However, as you've likely seen, using For cases where you absolutely need to access an entry point in For more details see https://dagger.dev/hilt/early-entry-point#background |
Thanks Brad for the detailed response, I totally missed
the HiltAndroidRule, makes sense now.
…On Fri, May 7, 2021 at 8:15 AM Brad Corso ***@***.***> wrote:
I think the way hilt works with tests isn't always intuitive. Like one
instance of application but different instance of SingletonComponent.
Where or when, or how such SingletonComponents are created by the way, if
not tied to Application#onCreate?
A lot of the complexity around testing is due to how Gradle runs
instrumentation tests. Gradle will run all tests using a single instance of
an Application. This means Application#onCreate() gets called only once
no matter how many tests and test cases you're running. It also means that
storing any state in the application class will leak that state across all
of your test cases. To avoid this issue, Hilt creates and stores the
SingletonComponent using the HiltAndroidRule rather than the Application
so that each test case gets its own component instance and is independent
from other test cases.
However, as you've likely seen, using HiltAndroidRule to create the
SingletonComponent causes issues if you try to call entry points from
Application#onCreate() because the SingletonComponent has not yet been
created (and even if you could create one, it's not clear which one you
would use since each test case has its own?).
For cases where you absolutely need to access an entry point in
Application#onCreate() we've created EarlyEntryPoint, but as you've
noticed, the binding is created from a completely different component that
has the lifetime of the Application rather than the HiltAndroidRule. In
general, you should avoid using EarlyEntryPoint unless you have no other
choice because it can lead to the issues you described of two instances of
a singleton component.
For more details see https://dagger.dev/hilt/early-entry-point#background
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2033 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAHU7IDBAXYWP7LBDBDMU63TMP7ZZANCNFSM4PZJJ64Q>
.
|
This is a pretty common issue from my experience, sometimes it works,
sometimes not.
What I normally do, is to uninstall everything from the device, maybe do a
clean and rebuild/reinstall. I think this has to do with the way Android
does partial apk updates in development.
…On Wed, Jul 7, 2021 at 6:59 AM Dawid Hyży ***@***.***> wrote:
I am getting
java.lang.RuntimeException: Unable to create application
com.example.HiltTestApplication_Application:
java.lang.IllegalStateException: The component was not created. Check that
you have added the HiltAndroidRule.
in the BaseApplication. Did you encounter that? My test class has
HiltAndroidRule
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2033 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAHU7IFSEVJEJB4XXALACZ3TWRMT3ANCNFSM4PZJJ64Q>
.
|
I've been trying to follow the steps above, but I'm getting the following error, when trying to access context from the application's onCreate. MainApplication @HiltAndroidApp
open class MainApplication : BaseApplication() { HiltTestApplication @CustomTestApplication(BaseApplication::class)
interface HiltTestApplication BaseApplication abstract class BaseApplication: Application()
lateinit var foo: Foo
@EarlyEntryPoint
@InstallIn(SingletonComponent::class)
internal interface ApplicationEarlyEntryPoint {
val foo: Foo
}
override fun onCreate() {
super.onCreate()
foo = EarlyEntryPoints.get(this <---- ERROR HERE, ApplicationEarlyEntryPoint::class.java).foo
...... Results in:
Is there a step I have missed somewhere? Thanks in advance! |
Can you check what your application class is when you're running? (Just throw in a break point or a print in there before you call |
Thanks @Chang-Eric for the suggestion. That helped me to spot that there was some misconfiguration in the CustomTestRunner. I updated that and applied the suggestion above to use the |
…ent GeneratedComponentManager in EarlyEntryPoints. Issue #2033. RELNOTES=n/a PiperOrigin-RevId: 468766554
…ent GeneratedComponentManager in EarlyEntryPoints. Issue #2033. RELNOTES=n/a PiperOrigin-RevId: 468809118
guys I am still getting following error
|
@kyodgorbek you will need to try the suggestions in the error message. If you have specific questions/issues please give more information. |
@bcorso I am doing as suggested but test fails |
@kyodgorbek, you're going to have to give more information for us to help you.
Also, since this issue is already closed, it's probably better to start a new issue with all of these details. |
I have an application that needs injection:
Other code in the app uses
application.printerFactory
.The
printerFactory
is provided by this:Now, for instrumented tests, I need to override this with a test version:
And so I get this error:
Well, I can't use HiltTestApplication because I need to use MyApplication, which has dependencies injected.
So I added this:
And so I get this error:
If
MyApplication
cannot be annotated with@HiltAndroidApp
, then how is it expected to inject things?Instructions unclear, ended up in inconsistent state.
The text was updated successfully, but these errors were encountered: