본문으로 건너뛰기
cccskills
무료GitHub에 공개

mockito-stale-mock-silent-trycatch-failure

Fix misleading Flutter/Dart test failures where expected data is empty ([]) or default values instead of the mocked response, caused by stale Mockito generated mocks or missing stubs being silently swallowed by try/catch in provider/repository code. Use when: (1) Test expects non-empty data but gets [], (2) Mock stubs for a method are set up but the code under test returns fallback/empty results, (3) A PR added new methods to a service class and tests that previously passed now fail with misleading "expected X, actual: empty" assertions, (4) "Generated files are out of date" CI error alongside test failures. The root cause is MissingStubError thrown by unstubbed mock methods, caught by production try/catch blocks, causing silent fallback to empty state.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md5.5 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Mockito Stale Mock + Silent Try/Catch Test Failures

Problem

When a PR adds new methods to a service class (e.g., AnalyticsApiService), and those methods are called from existing code paths that are wrapped in try/catch blocks, tests fail with misleading symptoms. The test assertion shows Expected: non-empty, Actual: [] or Expected: true, Actual: false — but the actual root cause is a MissingStubError being silently caught.

Context / Trigger Conditions

  • Symptom: Test expects data (non-empty list, specific values) but gets empty/default
  • No obvious error: No MissingStubError in test output because production code catches it
  • PR context: The PR added new methods to a @GenerateMocks-annotated service class
  • CI also shows: "Generated files are out of date" (stale .g.dart / .mocks.dart)
  • Existing tests were passing before the PR's changes

The Failure Chain

1. PR adds method `getBulkVideoViews()` to AnalyticsApiService
2. PR adds `_enrichVideosWithBulkStats()` to HomeFeedProvider.build()
3. _enrichVideosWithBulkStats calls getBulkVideoViews (new method)
4. Generated mock is stale — doesn't have getBulkVideoViews at all
5. Even if regenerated, test setUp doesn't stub getBulkVideoViews
6. Mock throws MissingStubError when unstubbed method is called
7. Provider's try/catch catches ALL exceptions (including MissingStubError)
8. Provider falls back to empty state → test sees [] instead of expected data

Solution

Step 1: Regenerate mocks

dart run build_runner build --delete-conflicting-outputs

This updates all .g.dart and .mocks.dart files to include new methods.

Step 2: Identify new method calls in modified code paths

Trace the code path from the test's entry point. Look for any method calls on mocked services that were added or modified by the PR. Pay special attention to methods called after the already-stubbed method (e.g., enrichment/post-processing steps).

Step 3: Add stubs for ALL methods in the call chain

// In test setUp():

// Existing stub (was already there)
when(mockService.getHomeFeed(
  pubkey: anyNamed('pubkey'),
  limit: anyNamed('limit'),
)).thenAnswer((_) async => HomeFeedResult(videos: mockVideos));

// NEW stubs needed for enrichment methods added by PR
when(mockService.getBulkVideoStats(
  argThat(isA<List<String>>()),  // non-nullable positional param
)).thenAnswer((_) async => <String, BulkVideoStatsEntry>{});

when(mockService.getBulkVideoViews(
  argThat(isA<List<String>>()),  // non-nullable positional param
  maxVideos: anyNamed('maxVideos'),
  maxConcurrent: anyNamed('maxConcurrent'),
)).thenAnswer((_) async => <String, int>{});

Step 4: Handle non-nullable parameters

Mockito's any returns null which fails for non-nullable params. Use:

  • argThat(isA<List<String>>()) for non-nullable positional params
  • anyNamed('paramName') works for named params with default values (but NOT for non-nullable named params without defaults)

Verification

After fixing:

  1. Run the specific test file: flutter test test/path/to/test.dart
  2. Confirm previously failing assertions now pass
  3. Run full test suite to check for no regressions

Example

Before (failing):

Expected: non-empty
  Actual: []

  test/providers/home_feed_loading_fix_test.dart 253:9

Root cause: _enrichVideosWithBulkStats called getBulkVideoStats (unstubbed) → MissingStubError → caught by provider try/catch → empty fallback state.

Fix: Added stubs in setUp() for getBulkVideoStats and getBulkVideoViews.

After (passing): All 11 tests pass, including the 2 that were failing.

Notes

  • Always regenerate mocks when a PR modifies @GenerateMocks-annotated classes. The .mocks.dart files must match the current interface.
  • Check the full call chain, not just the primary method. A getHomeFeed stub is useless if the provider then calls enrichVideos which calls 2 more unstubbed methods.
  • This pattern is invisible in test output. The MissingStubError is caught by production error handling. You'll never see it unless you add temporary print statements inside the catch block or use @GenerateNiceMocks (which returns defaults instead of throwing, potentially masking different bugs).
  • Non-nullable params in Dart null-safety: Mockito any returns null at the type level. For non-nullable positional params, use argThat(isA<Type>()) which returns a properly typed matcher.

References

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Fix ArgoCD ExternalSecret deployment failing with "namespace X is not permitted in project Y". Use when: (1) ExternalSecret shows OutOfSync in ArgoCD but won't sync, (2) ArgoCD application status shows "namespace X is not permitted in project 'infrastructure'", (3) ExternalSecret targets a namespace managed by a different ArgoCD project, (4) Using apps-of-apps pattern with separate infrastructure and application projects.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Art direction for any content — reads text, PDF, Word, HTML, PPT, then proposes 2-3 creative directions with photography style, mood, and visual language. After selection, generates AI image prompts and visual briefs section-by-section. Use when the user shares content and needs visual direction, image sourcing, or creative direction for any material.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Fix "Null check operator used on a null value" errors when an object is set to null during an async await. Use when: (1) Object reference is nullified while awaiting, (2) Code accesses object with ! after await returns, (3) Cancel/dispose operations run concurrently with async operations on same object. Solution: capture local reference before await.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Add custom metadata headers (x-amz-meta-*) to AWS v4 signed requests for GCS S3-compatible API. Use when: (1) Adding custom metadata to GCS uploads via S3 API, (2) Getting signature mismatch errors after adding new headers, (3) x-amz-meta-* headers being ignored or causing 403 errors. Custom headers MUST be included in canonical headers and signed headers list.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Fix password/secret authentication failures caused by trailing newlines when creating Google Cloud secrets (or similar) with bash here-strings. Use when: (1) Password authentication fails with correct password, (2) Secret created with `<<< "value"` syntax, (3) Error like "password authentication failed" or "invalid token" despite correct value. Bash here-strings (`<<<`) add a trailing newline that corrupts secrets.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Fix silent video/media processing failures caused by URL extraction code that filters on file extensions (.mp4, .webm, .webp). Use when: (1) Media moderation, transcoding, or analysis silently skips files from Blossom or content-addressed storage servers, (2) URL extraction from Nostr event tags (imeta, r tags) drops URLs without recognized extensions, (3) CDN fallback URLs append .mp4 but the actual server uses extensionless content-addressed paths like /{sha256}. Common in Nostr video events (kind 34236) where different clients use different URL formats.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

divinevideo의 스킬 모두 보기

이 스킬의 문제 신고