agentsclimarketplace

Mocking strategies

Skill almasumdev/awesome-mobile-testing-agent-skills/.github/skills/unit/mocking-strategies

Guidance on choosing between fakes, stubs, and mocking libraries (MockK, Mockito, mocktail, ts-jest, Swift test doubles) for mobile unit tests. Use when asked to mock a collaborator or to decide which double to use.From its SKILL.md

Install
npx -y skills add almasumdev/awesome-mobile-testing-agent-skills --skill mocking-strategies

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

4.7 KB, ~1.1k tokens by cl100k_base, as published. Nobody here has run it

Mocking Strategies for Mobile

Instructions

Most "mocking" problems are really design problems: the code under test depends on a concrete implementation instead of an interface, or on a framework class it does not own. Fix the seam first; pick the double second.

1. Hierarchy of Test Doubles

From most preferred to least:

  1. Fake — a working in-memory implementation of an interface (e.g., InMemoryUserRepository). Durable, readable, reusable across tests.
  2. Stub — returns hard-coded answers to method calls, no behavior.
  3. Spy — a real object with selected method interactions recorded.
  4. Mock — a test-only object whose interactions are asserted.

Default to fakes for stateful collaborators (repositories, caches, stores) and stubs/mocks only for thin boundary adapters (loggers, analytics, network clients).

2. Do Not Mock What You Do Not Own

Never mock OkHttpClient, URLSession, Dio, fetch, Firebase*, CoreLocation, etc. directly. Wrap them in a seam you own:

interface WeatherApi { suspend fun forecast(city: String): Forecast }
class OkHttpWeatherApi(private val client: OkHttpClient) : WeatherApi { /* ... */ }

Then fake or mock WeatherApi in tests. The wrapper is tested once as an integration test.

3. Library Choices

Kotlin — MockK (preferred) for idiomatic Kotlin including coroutines and top-level functions:

val repo = mockk<UserRepository>()
coEvery { repo.load(id = 42) } returns User("Ada")

Java — Mockito / Mockito-Kotlin:

UserRepository repo = mock(UserRepository.class);
when(repo.load(42)).thenReturn(new User("Ada"));

Swift — manual protocol doubles (preferred):

final class UserRepositoryFake: UserRepository {
    var users: [Int: User] = [:]
    func load(id: Int) async throws -> User {
        guard let u = users[id] else { throw UserError.notFound }
        return u
    }
}

Use Cuckoo or Mockingbird only when the codebase is already committed to them; hand-written doubles usually win on readability.

Dart — mocktail (null-safe, no codegen):

class MockUserRepo extends Mock implements UserRepository {}

final repo = MockUserRepo();
when(() => repo.load(42)).thenAnswer((_) async => User('Ada'));

Prefer mocktail over mockito in new Flutter code — no build_runner step.

TypeScript / Jest:

const repo: jest.Mocked<UserRepository> = {
  load: jest.fn().mockResolvedValue({ name: 'Ada' }),
};

For module-level mocks prefer jest.mock('./module', factory) over auto-mocking; auto-mocks silently drift from the real module.

4. Verify vs Assert

Assert on outputs and observable state by default:

val result = sut.signIn(email, password)
result shouldBe Success(userId = 1)

Verify interactions only when the interaction is the behavior:

verify(exactly = 1) { analytics.track(SignInSucceeded(userId = 1)) }

Every verify you write is a line the next refactor might break without the behavior actually changing. Treat them as expensive.

5. Argument Matchers and Captors

Match only on what matters. any() everywhere hides bugs; over-specific matchers make tests brittle. When you need to inspect a complex argument, use a captor:

val slot = slot<AnalyticsEvent>()
verify { analytics.track(capture(slot)) }
slot.captured.name shouldBe "sign_in_succeeded"

6. Time, Randomness, IDs

These are collaborators. Inject them instead of mocking statics:

class OrderFactory(private val clock: Clock, private val ids: IdGenerator)

Then pass a FixedClock(Instant.parse("2026-01-01T00:00:00Z")) and SequentialIdGenerator() in tests.

7. Common Smells

  • Mocking Logger / println — usually unnecessary; if you must, assert on a collected log list, not on call counts.
  • Mocking DateTime.now() by patching a global — inject a clock instead.
  • Five-deep when { ... } chains — the SUT is doing too much; split it.
  • Matching on anyOrNull() for every argument — the test asserts nothing.

8. Checklist

  • Third-party SDKs are wrapped before being faked.
  • Fakes are used for stateful collaborators; mocks only for interaction verification.
  • No static/global clock, RNG, or ID generator reaches the SUT.
  • verify calls exist only where the interaction is the behavior.
  • Argument matchers are specific enough to catch regressions.
  • Chosen library matches the stack (MockK / Mockito / mocktail / Jest / hand-written Swift doubles).

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,782. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.