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:
- Open IntelliJ IDEA → Settings → Plugins → Marketplace
- Search "Diffblue Cover"
- 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 --versionThe CLI requires Java 11+ on the path and a valid license key set via environment variable:
export DIFFBLUE_COVER_LICENSE_KEY=your-key-hereProject 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-runThe 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:
- Run
dcover create --module your-moduleon a feature branch - Run the tests:
mvn test - Review the diff — delete low-value tests, improve assertions on important ones
- 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.htmlA 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.