Getting Started with Diffblue Cover: AI-Generated Unit Tests for Java

Getting Started with Diffblue Cover: AI-Generated Unit Tests for Java

Writing unit tests is one of the most time-consuming parts of Java development — and one of the most frequently skipped. Diffblue Cover addresses this by generating JUnit tests automatically using AI. This guide walks through installation, first run, and what to do with the tests it produces.

What Diffblue Cover Actually Does

Diffblue Cover analyzes compiled Java bytecode, infers what each method does, and writes JUnit 5 tests that exercise that behavior. It uses a combination of static analysis and a reinforcement-learning model trained on millions of Java test cases.

The output is real, runnable JUnit code — not pseudo-code, not stubs. The tests use standard mocking libraries (Mockito by default) and compile against your existing dependencies.

Critically, it writes tests that pass against the current codebase. This is both its strength and something to be aware of: it captures existing behavior, not intended behavior. More on that distinction shortly.

Installation

Diffblue Cover is distributed as an IntelliJ IDEA plugin and as a CLI tool (dcover) for CI use. For local development, start with the plugin.

IntelliJ Plugin:

  1. Open IntelliJ IDEA → Settings → Plugins → Marketplace
  2. Search "Diffblue Cover"
  3. Install and restart

You'll need a license. Diffblue offers a free Community edition with limits on the number of tests generated per run and no CI access. The Team and Enterprise tiers lift these limits and unlock the dcover CLI.

CLI Install (Linux/macOS):

# Download the dcover CLI
curl -L https://releases.diffblue.com/cover/latest/dcover -o dcover
chmod +x dcover
sudo mv dcover /usr/local/bin/

# Verify
dcover --version

The CLI requires Java 11+ on the path and a valid license key set via environment variable:

export DIFFBLUE_COVER_LICENSE_KEY=your-key-here

Project Requirements

Diffblue Cover works on compiled bytecode, so your project must build successfully before it can generate tests. Requirements:

  • Java 8, 11, 17, or 21
  • Maven or Gradle build system
  • JUnit 5 (or JUnit 4 — Cover supports both, but JUnit 5 output is default)
  • Mockito on the test classpath

If your project doesn't have Mockito yet, add it:

<!-- Maven -->
<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-core</artifactId>
  <version>5.11.0</version>
  <scope>test</scope>
</dependency>

Running Diffblue Cover for the First Time

Via IntelliJ

Right-click any class in the project tree → Diffblue Cover → Write Tests for Class.

Cover analyzes the class, generates tests, and opens a diff view showing what it produced. You can accept all, reject all, or cherry-pick individual test methods.

For a whole module: right-click the module → Diffblue Cover → Write Tests for Module. This can take several minutes on large codebases.

Via CLI

# Build first
mvn compile test-compile -DskipTests

# Generate tests for a single class
dcover create --class com.example.OrderService

# Generate tests for an entire module
dcover create --module order-service

# Dry run — shows what would be created without writing files
dcover create --module order-service --dry-run

The CLI writes test files directly into src/test/java, mirroring your main source tree.

What the Generated Tests Look Like

Here's a representative example. Given this service class:

@Service
public class OrderService {

    private final OrderRepository orderRepository;
    private final InventoryClient inventoryClient;

    public OrderService(OrderRepository orderRepository, InventoryClient inventoryClient) {
        this.orderRepository = orderRepository;
        this.inventoryClient = inventoryClient;
    }

    public Order createOrder(String productId, int quantity) {
        if (!inventoryClient.isAvailable(productId, quantity)) {
            throw new InsufficientInventoryException(productId);
        }
        Order order = new Order(productId, quantity, OrderStatus.PENDING);
        return orderRepository.save(order);
    }
}

Diffblue Cover generates something like:

@ExtendWith(MockitoExtension.class)
class OrderServiceDiffblueTest {

    @Mock
    private InventoryClient inventoryClient;

    @Mock
    private OrderRepository orderRepository;

    @InjectMocks
    private OrderService orderService;

    @Test
    void testCreateOrder() {
        // Arrange
        when(inventoryClient.isAvailable("42", 1)).thenReturn(true);
        Order savedOrder = new Order("42", 1, OrderStatus.PENDING);
        when(orderRepository.save(any(Order.class))).thenReturn(savedOrder);

        // Act
        Order result = orderService.createOrder("42", 1);

        // Assert
        verify(orderRepository).save(any(Order.class));
        assertEquals("42", result.getProductId());
        assertEquals(OrderStatus.PENDING, result.getStatus());
    }

    @Test
    void testCreateOrderThrowsWhenInventoryUnavailable() {
        // Arrange
        when(inventoryClient.isAvailable("42", 1)).thenReturn(false);

        // Act & Assert
        assertThrows(InsufficientInventoryException.class,
            () -> orderService.createOrder("42", 1));
    }
}

The tests are readable, follow standard patterns, and cover both the happy path and the exception branch.

Reviewing Generated Tests

Not everything Cover generates is worth committing. Review criteria:

Keep tests that:

  • Cover non-trivial branches (null checks, exception paths, conditional logic)
  • Test public API surface that wasn't tested before
  • Use realistic-looking argument values

Delete or revise tests that:

  • Assert on implementation details likely to change (exact mock invocation counts on internal helpers)
  • Use magic constants with no semantic meaning ("foo", 42)
  • Duplicate what you've already written manually

Add assertions where Cover was conservative. Cover sometimes writes tests that verify a method doesn't throw but doesn't assert the return value. That's a starting point, not a finished test.

Committing the Tests

Treat generated tests like generated code: review them in a PR before merging. A useful workflow:

  1. Run dcover create --module your-module on a feature branch
  2. Run the tests: mvn test
  3. Review the diff — delete low-value tests, improve assertions on important ones
  4. Commit with a message like test: add Diffblue-generated unit tests for OrderService

Don't batch-commit thousands of generated tests without review. Fifty high-quality tests you understand are worth more than five hundred you don't.

Coverage Baseline

After committing, check your coverage delta:

mvn test jacoco:report
open target/site/jacoco/index.html

A first Diffblue run on an untested module typically moves coverage from near 0% to 60–80% on service and utility classes. The remaining gap is usually complex integration paths, edge cases with external state, and business logic that requires domain knowledge to test well.

What's Next

Unit tests generated by Diffblue Cover tell you a class behaves consistently with itself. They don't tell you whether the application works correctly end-to-end. For browser-based and API-level testing — confirming that your Order API actually processes a payment and emails the customer — tools like HelpMeTest cover that layer without requiring code.

The two approaches complement each other: Diffblue handles the unit layer at scale, and end-to-end tests verify the system works as a whole.

Read more

Start now free