
Write Unit Tests First and Use AI to Generate Code That Passes Them
Introduction
Many developers turn to AI to generate unit tests for existing code, but what if we flipped the process? Instead of using AI to write tests, we can write unit tests first and use AI to generate the implementation that satisfies them. This approach aligns with test-driven development (TDD), ensuring that our code meets predefined requirements and is robust from the start.
In this article, we’ll explore how to use Java with Gradle and JUnit to write unit tests first, then leverage AI to generate code that fulfills the tests.
Why Write Tests First?
Writing tests before implementation has several advantages:
- Ensures clarity: Defines what the code should do before implementation.
- Improves reliability: Helps prevent regressions by ensuring new changes don’t break existing functionality.
- Encourages modular design: Leads to better-structured and maintainable code.
- Guides AI code generation: AI can focus on meeting the test requirements instead of generating arbitrary code.
Let’s see this in action.
Setting Up Java, Gradle, and JUnit
To follow along, ensure you have:
- Java installed (JDK 11+ recommended)
- Gradle installed
- An AI coding assistant (like ChatGPT or GitHub Copilot)
Create a new Gradle project and add JUnit dependencies.
build.gradle
(Kotlin DSL)
plugins {
id 'java'
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2'
}
test {
useJUnitPlatform()
}
Step 1: Writing Unit Tests First
We’ll start by writing a simple unit test for a Calculator
class that doesn’t exist yet.
CalculatorTest.java
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
@Test
void testAddition() {
Calculator calculator = new Calculator();
assertEquals(5, calculator.add(2, 3));
}
@Test
void testSubtraction() {
Calculator calculator = new Calculator();
assertEquals(1, calculator.subtract(3, 2));
}
}
This test defines a Calculator
class with add
and subtract
methods, even though it hasn’t been implemented yet.
Step 2: Using AI to Generate Code That Passes the Tests
Now, we can ask AI to generate a Calculator
class that satisfies the tests.
AI-Generated Calculator.java
class Calculator {
public int add(int a, int b) {
return a + b;
}
public int subtract(int a, int b) {
return a - b;
}
}
By running our tests, we ensure the AI-generated code meets the expected functionality.
Step 3: Running the Tests
Run the tests using Gradle:
gradle test
Expected output:
BUILD SUCCESSFUL
If the tests pass, we have successfully driven our development using tests first!
Scaling Up: More Complex Examples
Now that we have a basic example, let’s scale up with another feature: a method to calculate factorial.
New Test in CalculatorTest.java
@Test
void testFactorial() {
Calculator calculator = new Calculator();
assertEquals(120, calculator.factorial(5));
}
AI-Generated Code:
class Calculator {
public int factorial(int n) {
if (n == 0) return 1;
return n * factorial(n - 1);
}
}
Running gradle test
again should pass all tests.
Conclusion
By writing tests first and using AI to generate the implementation, we:
- Ensure that our code meets clear, testable requirements.
- Avoid unnecessary or overly complex AI-generated code.
- Maintain a structured, test-driven approach to development.
Next time you’re using AI in coding, try flipping the process—write the test cases first and let AI fill in the gaps!