Skip to main content

Trace-Based Java Testing: Deterministic Verification without Mocks

· 8 min read
Dmitry Turmyshev
Product Manager | Developer Experience and Software Quality

BitDive Unit Test Creation UI - Generating deterministic JUnit tests from real Java application behavior

Real Runtime Data is the Ultimate Source of Truth. Writing unit tests manually is a losing battle against technical debt. BitDive captures the actual execution of your code and transforms it into Deterministic Verification suites, eliminating the need for manual mocking and giving you the Real Runtime Data required for AI-native development.


The Problem with Traditional Test Automation

Modern test automation has a hidden cost. Tools like Cypress, Playwright, and Mockito require QA engineers and developers to be full-time test maintainers. Even if AI writes the initial code, someone must review it, debug it, and fix it after every refactoring. The test framework becomes a "second product" that consumes resources.

Sure, you can use AI to create hundreds of tests in seconds. But then what? You're stuck debugging flaky tests, fixing brittle assertions, and updating mocks every time your API changes. AI-generated tests are probabilistic, they are guesses based on static code, often missing the nuance of production data.

Even worse, traditional tests operate in a vacuum. They mock databases with "happy path" data and simulate services based on outdated assumptions. When they fail, you're left hunting through logs, attempting to reconstruct what happened.

A Different Approach: Trace-Based Testing

BitDive replaces manual test writing and AI guessing with a Trace-Based Testing process that creates robust unit and integration tests:

  1. Capture: Record the actual execution of key scenarios inside your service (from production or test environments).
  2. Replay: Turn these recordings into standard JUnit 5 tests that act as a regression baseline.

The key difference? BitDive doesn't guess what your code should do. It records what it actually did and verifies that behavior remains consistent. This is Deterministic Testing.

How It Works: Creating a Test from a Trace

Let's walk through the process using a real demo application.

The Setup

The process is simple: use your application naturally, and BitDive captures everything. In our walkthrough video, we use a sample Spring Boot microservices application (Faculty, Report, and OpenAI services with Kafka and PostgreSQL), but the approach works for any Java application.

Step 1: Capture Execution

Open your application and perform typical operations. BitDive runs in the background, capturing the full execution context: method calls, SQL queries, dependency interactions, and API responses.

After using the application, open the BitDive workspace. You'll see all microservices that participated in your actions, along with infrastructure dependencies like databases and message brokers.

BitDive Microservices Topology - Visualizing service dependencies and performance heatmaps for unit test context

Step 2: Debug Failures (Optional)

When something fails, you get immediate debugging insight. For example, if an operation returns a 500 error, open that call in the HeatMap and drill down to the exact method and SQL query that caused it. This is Runtime Observability, seeing the code execute method-by-method.

SQL Performance Analysis - Debugging database exceptions and slow queries within Java method traces

Step 3: Select Operations for Testing

Go to the Testing tab and create a new test. Select the operations you want to preserve as regression tests. Complex calls like "GetStudentsForCourseReport" make excellent candidates because they exercise deep call chains and multiple dependencies.

Trace Selection Interface - Choosing production executions to convert into automated regression tests

Step 4: Inspect Execution Details

For each operation, choose the specific recorded trace. You can inspect it in detail: input arguments, return values, DTOs passed between services, and SQL queries executed against the database.

BitDive Trace Drilldown - Inspecting method-level input arguments and JSON return values for verification

This transparency lets you verify you're capturing the right baseline behavior before validating it.

Step 5: Create the JUnit Test

Once you've selected your traces, click "Create Test". BitDive packages the recorded execution into a JSON Replay Plan with exact inputs, exact outputs, and exact dependency interactions.

Deterministic Test Generation - Packaging recorded Java execution data into a JSON replay plan

Crucially, BitDive automatically virtualizes the environment. When the code tries to hit the database during replay, BitDive intercepts the call and returns the recorded response. This effectively "auto-mocks" your dependencies using real data.

The End of Manual Mocking? Why Recorded Data Wins

Manual mocking (like Mockito) is a "best guess" of dependency behavior. It often leads to tests that pass in CI but fail in production because the mock didn't account for real-world database constraints or API edge cases.

BitDive solves this with Real Runtime Data:

  • No Manual Mocks: BitDive records the actual response from PostgreSQL, Kafka, or S3.
  • Full Trace Integrity: The validation includes the SQL statement, the parameters, and the exact sequence of calls.
  • Zero Drift: As your production data changes, you simply recapture the trace to update the test.

Step 6: Run as Standard Java Unit Tests

Copy the test identifier and paste it into your IDE's JUnit configuration. Then run:

mvn test

Maven Test Execution - Success report for trace-based JUnit suites in a CI/CD pipeline

A single complex operation might create many individual validation checks because BitDive verifies behavior across the entire call chain, method by method.

Step 7: Review Results

To see detailed validation, run the test in your IDE. You can walk through each method and see what was expected versus what actually came back.

IDE Test Comparison - Analyzing deviations between expected baseline and actual Java code execution

Strategic Advantages of Trace-Based Testing

This approach delivers benefits that go beyond just saving time:

1. Zero Manual Code Means Zero Debt

Your test coverage grows based on product usage, not test code. You can achieve high coverage without managing a massive repository of test scripts. When logic changes legitimately, you capture new behavior instead of rewriting code.

2. White Box Visibility Without Logs

Traditional tests check HTTP status codes. BitDive validates the internal execution path: same SQL queries, same method calls, same dependency interactions. When a test fails, you don't hunt through logs. You see the exact divergence point in the trace.

This creates a "shared reality" between QA and developers. A failing test includes a link to the exact trace showing where behavior changed.

3. Flexible Verification Modes

BitDive offers flexible replay:

  • Replay Mode: All external dependencies (databases, APIs, Kafka) are replayed from captured traces. Zero infrastructure, instant validation. Runs anywhere with mvn test.
  • Testcontainers Mode: Real databases via Testcontainers with external APIs still replayed from traces. Catches schema drift and real SQL behavior.

You choose the trade-off between speed and depth per test.

Ready to Try Trace-Based Testing?

Follow our step-by-step documentation to set up your first replay test in minutes.

Read Documentation

4. Protection Against Unexpected Behavior

Standard tests verify "200 OK". BitDive validates the entire execution path: who called who, in what order, with what parameters.

If a developer refactors the code and accidentally changes the query logic (for example, removing a join or altering a WHERE condition), the test will fail even if the HTTP response looks correct. These are the subtle unexpected behavior changes that slip past status-code tests and cause production incidents weeks later.

The Bottom Line

Writing unit tests shouldn't require writing unit test code. Your application's real behavior is the gold standard. Capture it. Replay it. Validate it. That's the essence of Trace-Based Testing.

Choosing the Right Automation Strategy

While BitDive focuses on runtime recording, understanding how it compares to other tools is key:


👉 Start Creating Trace-Based Tests

👉 Browse the BitDive Engineering Glossary