How does Mockito.mock create a mock at the bytecode level, and what limits exist for final classes, final methods, and static methods?
answer
- runtime subclass via ByteBuddy + MockMethodInterceptor
- MockMaker is a pluggable SPI; Objenesis skips constructor
- subclass maker can't do final/static/private
- inline maker (default in Mockito 5) rewrites bytecode -> final/static OK
- mockStatic/mockConstruction are scoped, must be closed
basics
~20 sMockito generates the mock at runtime by subclassing the type (using ByteBuddy) and overriding its methods. The default subclass approach can't override final classes/methods or static methods. The newer inline mock maker (default in Mockito 5) can mock final and static via mockStatic.
solid answer
~50 sMockito creates a mock by dynamically generating a subclass of the target type at runtime and routing every method through its own interceptor that records the call and returns the stubbed-or-default value. The engine doing the bytecode generation is ByteBuddy, behind a pluggable component called the MockMaker. The classic subclass-based mock maker can mock interfaces and non-final classes by extending them, but it fundamentally cannot override what the JVM won't let you override: final classes, final methods, and static methods, plus constructors and private methods. The inline mock maker (mockito-inline, and the default since Mockito 5.0 on supported JVMs) uses the JVM's instrumentation API to redefine classes in place, which lets it mock final classes/methods, and provides mockStatic(...)/mockConstruction(...) for statics and constructors. Even so, mocking statics/finals is a smell often signalling a hard-to-test design; wrapping the dependency behind an interface you own is usually cleaner. Each mock also carries per-instance state (recorded invocations, stubs), which is why cleanup matters with the inline maker.
go deeper
Aware that Mockito generates the mock at runtime and that some things (statics) can't be mocked with plain mock().
Knows the subclass approach can't mock final/static and that mockStatic exists for statics.
Explains ByteBuddy subclassing vs the inline mock maker's class redefinition, the final/static/constructor matrix, and scope/cleanup of MockedStatic.
Treats static/final mocking as a design signal, drives DI-based seams over instrumentation, and weighs MockMaker choice, agent/instrumentation constraints, and suite-wide leak/cleanup implications.
## What 'create a mock' actually does When you call `Mockito.mock(OrderRepository.class)`, Mockito must hand you back an object that (a) **is** an `OrderRepository` (assignable to that type) and (b) intercepts every method call so it can record it and return a stubbed or default value. Java has no built-in 'fake any type' facility, so Mockito *generates code at runtime*. ## ByteBuddy and the MockMaker The component responsible is the **`MockMaker`** — a pluggable SPI. The default implementations use **ByteBuddy**, a runtime bytecode-generation library, to synthesize a new class. Two strategies exist: ### 1. Subclass mock maker (the classic default) ByteBuddy generates a **subclass** of the target (or, for an interface, a class implementing it) and overrides every method to delegate to a `MockMethodInterceptor`. The interceptor consults the mock's stubbing rules and default `Answer`. Because it relies on **overriding**, it inherits the JVM's overriding rules: - **Interfaces** — fine (implement them). - **Non-final concrete/abstract classes** — fine (subclass them); Mockito uses Objenesis to instantiate without calling a constructor. - **`final` classes** — *cannot* be subclassed → not mockable. - **`final` methods** — *cannot* be overridden → calls hit the real implementation, not the mock. - **`static` methods** — not associated with an instance, so a subclass override is impossible. - **`private` methods / constructors** — not overridable. ### 2. Inline mock maker (default since Mockito 5.0) The **inline mock maker** (formerly the separate `mockito-inline` artifact, now the default on supported JDKs in Mockito 5) takes a different route: it uses the JVM **instrumentation / class-redefinition** API (a Java agent) to **rewrite the bytecode of the real class in place**, inserting interception hooks. Because it modifies the class itself rather than subclassing, it can intercept **`final` classes and `final` methods**, and it enables: - **`mockStatic(SomeClass.class)`** → returns a `MockedStatic` (scoped, usually in try-with-resources) that lets you stub static methods *for the current thread only*. - **`mockConstruction(SomeClass.class)`** → intercepts `new SomeClass(...)` within a scope. ```java try (MockedStatic<UUID> uuids = mockStatic(UUID.class)) { uuids.when(UUID::randomUUID).thenReturn(fixedId); // code under test sees fixedId } // static stubbing removed here ``` These scoped mocks must be **closed** (try-with-resources) so the static/constructor stubbing doesn't leak into other tests on the same thread. ## Per-instance state and cleanup Each mock holds its own recorded invocations and stubbings. With the inline mock maker, mocks are tracked via strong references during their scope; failing to close `@Mock` resources (`openMocks(...).close()`) or `MockedStatic` scopes can leak memory across a large suite — which is why `openMocks` returns an `AutoCloseable` and why static/construction mocks are scoped. ## Why limits matter — and the design signal Before the inline maker, 'can't mock final/static' was a hard wall that pushed teams toward testable design: depend on **interfaces you own**, wrap third-party static/final APIs behind a thin seam, and inject `Clock`/`Supplier<UUID>` instead of calling `Instant.now()`/`UUID.randomUUID()` directly. Even now that the inline maker *can* mock these, doing so is frequently a **code smell**: reaching for `mockStatic` often means a dependency wasn't injected. The mature stance is: it's available, use it for legacy/third-party code you can't change, but prefer dependency injection and small seams so plain `mock(Class)` suffices. ## Summary table | Target | Subclass maker | Inline maker | |---|---|---| | interface | yes | yes | | non-final class | yes | yes | | final class | no | yes | | final method | no (runs real) | yes | | static method | no | yes (mockStatic) | | constructor | no | yes (mockConstruction) | | private method | no | no |
- Why does mocking a static method require mockStatic rather than mock(Class)?Static methods aren't tied to an instance, so a generated subclass can't override them. The inline mock maker rewrites the class itself and exposes a scoped MockedStatic, which intercepts static calls on the current thread until the scope closes.
- You need to control UUID.randomUUID() in a test. What's the cleaner alternative to mockStatic?Inject a seam — e.g. a Supplier<UUID> or a custom IdGenerator interface — and mock that with plain mock(Class). It avoids static mocking entirely and makes the dependency explicit.
saying these in an interview costs you the question
- Saying plain mock(Class) can mock static methods (needs mockStatic / inline maker)
- Claiming final classes were never mockable (the inline maker handles them)
- Forgetting to close MockedStatic/MockedConstruction scopes, leaking stubs across tests
- Thinking the mock runs real code by default — interception returns stubs/defaults, not real logic