<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Evgeniy Bobrovnichiy</title>
    <description>The latest articles on DEV Community by Evgeniy Bobrovnichiy (@evgeniy_bobrovnichiy).</description>
    <link>https://dev.to/evgeniy_bobrovnichiy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4053572%2F5c68d242-2264-4bb2-b53b-ccba6aa0361b.jpg</url>
      <title>DEV Community: Evgeniy Bobrovnichiy</title>
      <link>https://dev.to/evgeniy_bobrovnichiy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/evgeniy_bobrovnichiy"/>
    <language>en</language>
    <item>
      <title>The Last Error Is Not Always the Root Cause: Diagnosing Android Test Failures</title>
      <dc:creator>Evgeniy Bobrovnichiy</dc:creator>
      <pubDate>Wed, 29 Jul 2026 16:18:56 +0000</pubDate>
      <link>https://dev.to/evgeniy_bobrovnichiy/the-last-error-is-not-always-the-root-cause-diagnosing-android-test-failures-o90</link>
      <guid>https://dev.to/evgeniy_bobrovnichiy/the-last-error-is-not-always-the-root-cause-diagnosing-android-test-failures-o90</guid>
      <description>&lt;p&gt;An Android UI test times out. Maestro reports that an element is missing. A screenshot shows a blank or half-rendered screen.&lt;/p&gt;

&lt;p&gt;It is tempting to fix the selector, increase the timeout, and rerun the test.&lt;/p&gt;

&lt;p&gt;But those are often secondary symptoms. The application may have crashed several events earlier.&lt;/p&gt;

&lt;p&gt;This article uses a synthetic UK weather forecast application to show how I separate the first actionable failure from the noise that follows it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The failure
&lt;/h2&gt;

&lt;p&gt;Assume the test artifact contains this stack trace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fatal Exception: java.lang.IllegalStateException:
No binding for class
dev.flakylens.samples.weatheruk.forecast.ForecastDetailsViewModel provided.
See documentation for ViewModelProviderFactory

at dev.flakylens.samples.weatheruk.di.AssistedViewModelFactoryRegistry.factoryFor(
    AssistedViewModelFactoryRegistry.kt:37)
at dev.flakylens.samples.weatheruk.forecast.ForecastDetailsComponent(
    ForecastDetailsComponent.kt:51)
at dev.flakylens.samples.weatheruk.WeatherForecastActivity.Content(
    WeatherForecastActivity.kt:59)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A UI-test report generated later might contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Element not found: "Tomorrow's forecast"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both messages are real, but they are not equally useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reconstruct the failure chain
&lt;/h2&gt;

&lt;p&gt;Read the evidence in chronological and causal order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;WeatherForecastActivity&lt;/code&gt; renders the forecast details component.&lt;/li&gt;
&lt;li&gt;The component requests &lt;code&gt;ForecastDetailsViewModel&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AssistedViewModelFactoryRegistry.factoryFor()&lt;/code&gt; cannot find a compatible binding.&lt;/li&gt;
&lt;li&gt;The application throws &lt;code&gt;IllegalStateException&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The activity stops rendering.&lt;/li&gt;
&lt;li&gt;The UI-test runner eventually reports a missing element or timeout.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The missing element is therefore an effect of the crash. Changing the selector cannot restore a ViewModel binding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate evidence from inference
&lt;/h2&gt;

&lt;p&gt;Good diagnostics should make this boundary explicit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct evidence
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Exception: &lt;code&gt;java.lang.IllegalStateException&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Message: no binding exists for &lt;code&gt;ForecastDetailsViewModel&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;First relevant application frame: &lt;code&gt;AssistedViewModelFactoryRegistry.kt:37&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ViewModel request site: &lt;code&gt;ForecastDetailsComponent.kt:51&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Host activity: &lt;code&gt;WeatherForecastActivity.kt:59&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Likely inference
&lt;/h3&gt;

&lt;p&gt;The host activity probably supplies a generic or incompatible &lt;code&gt;ViewModelProvider.Factory&lt;/code&gt; for a ViewModel that requires assisted arguments.&lt;/p&gt;

&lt;p&gt;This is not proven by the stack trace alone. Repository context is needed to verify which factory the activity uses and whether another working activity already demonstrates the correct pattern.&lt;/p&gt;

&lt;p&gt;That distinction matters. A diagnostic tool should not present a plausible guess as a verified fact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find the repair pattern in the repository
&lt;/h2&gt;

&lt;p&gt;Search the codebase for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the missing ViewModel class;&lt;/li&gt;
&lt;li&gt;its assisted factory or generated binding;&lt;/li&gt;
&lt;li&gt;the activity from the stack trace;&lt;/li&gt;
&lt;li&gt;another activity that creates a similar ViewModel successfully;&lt;/li&gt;
&lt;li&gt;overrides of &lt;code&gt;defaultViewModelProviderFactory&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project-specific API may differ, but the repair often resembles this pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Inject&lt;/span&gt;
&lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;assistedFactoryProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AssistedViewModelFactoryProvider&lt;/span&gt;

&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;defaultViewModelProviderFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModelProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Factory&lt;/span&gt;
    &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assistedFactoryProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extras&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is not copying this snippet blindly. Confirm that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the missing ViewModel is registered with the assisted provider;&lt;/li&gt;
&lt;li&gt;the host supplies the required owner and arguments;&lt;/li&gt;
&lt;li&gt;the returned factory is used by the component requesting the ViewModel;&lt;/li&gt;
&lt;li&gt;the implementation matches an established pattern in the same repository.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Verify the fix
&lt;/h2&gt;

&lt;p&gt;After changing the factory:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the smallest test that opens the affected screen.&lt;/li&gt;
&lt;li&gt;Capture Logcat from before the navigation begins.&lt;/li&gt;
&lt;li&gt;Confirm that the original &lt;code&gt;IllegalStateException&lt;/code&gt; is gone.&lt;/li&gt;
&lt;li&gt;Confirm that &lt;code&gt;ForecastDetailsViewModel&lt;/code&gt; is created successfully.&lt;/li&gt;
&lt;li&gt;Only then investigate any remaining selector or timeout failure.&lt;/li&gt;
&lt;li&gt;Add a regression test that opens the activity with the required arguments.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This order prevents a secondary UI error from hiding a still-active application crash.&lt;/p&gt;

&lt;h2&gt;
  
  
  A reusable triage checklist
&lt;/h2&gt;

&lt;p&gt;When an Android UI test produces several failures, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is the earliest fatal exception?&lt;/li&gt;
&lt;li&gt;What is the first application-owned stack frame?&lt;/li&gt;
&lt;li&gt;Which later failures require the application to have remained alive?&lt;/li&gt;
&lt;li&gt;Is the proposed cause directly supported or inferred?&lt;/li&gt;
&lt;li&gt;Can repository code confirm the suspected wiring problem?&lt;/li&gt;
&lt;li&gt;Is there a working implementation of the same pattern nearby?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach applies beyond ViewModel factories. It is also useful for dependency-injection failures, malformed API responses, missing navigation arguments, startup crashes, and mock-server mismatches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I built FlakyLens
&lt;/h2&gt;

&lt;p&gt;I kept repeating this reconstruction manually across Logcat, Maestro, JUnit, CI output, and source code. I built &lt;strong&gt;FlakyLens&lt;/strong&gt; to make the process faster and more explicit.&lt;/p&gt;

&lt;p&gt;It accepts pasted Logcat text and &lt;code&gt;.txt&lt;/code&gt;, &lt;code&gt;.log&lt;/code&gt;, &lt;code&gt;.xml&lt;/code&gt;, &lt;code&gt;.json&lt;/code&gt;, &lt;code&gt;.yaml&lt;/code&gt;, &lt;code&gt;.yml&lt;/code&gt;, and &lt;code&gt;.zip&lt;/code&gt; artifacts. The report identifies the likely primary failure, preserves supporting evidence, separates inference from facts, and can use an optional read-only GitHub connection to produce repository-aware recommendations.&lt;/p&gt;

&lt;p&gt;Raw uploaded artifacts are processed in the browser. A demo is available without signing in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://flakylens.dev/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=founding_beta_launch" rel="noopener noreferrer"&gt;Try the FlakyLens demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Founding Beta is free for the first 500 developers through January 29, 2027. No card is required and there is no automatic paid conversion.&lt;/p&gt;

&lt;p&gt;Disclosure: I built FlakyLens, and the weather application and stack trace in this article are synthetic examples created for demonstration.&lt;/p&gt;

&lt;p&gt;What Android failure class should I cover next: dependency injection, mock-server mismatches, Compose crashes, or CI-only timeouts?&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>testing</category>
      <category>debugging</category>
    </item>
  </channel>
</rss>
