Lab 5: Testing with JUnit¶
In this lab, you will implement a CarTest class that tests the Car class from Lab 3.
You will gain experience with JUnit and code coverage.
Learning Objectives¶
After completing this lab, you should be able to:
- Write JUnit tests that create objects and assert non-static methods.
- Run JUnit tests in VS Code (from the test class and Testing activity).
- Analyze code coverage results to find out which lines are not tested.
Time to Learn, Not to Vibe ✨
You're learning today how to think like a software developer and how to write tests that cover a wide range of possible input cases.
Thus, please do not use AI to generate unit tests for you! However, we will have examples of how to use AI to help think about different possible test cases.
We recommend using Microsoft Copilot Chat (free for all JMU students). You are free to use other AI tools if you prefer.
Introduction to JUnit¶
JUnit is a widely used open-source testing framework for Java. When writing tests, developers generally follow a basic pattern:
- For every class C, there is a class named
CTestfor testing the C class. - For every method m, there is a method named
testMfor testing the m method. - Additional test methods can be written to test specific requirements.
Compare the "Main Class" and "Test Class" examples below (click to open):
Main Class
public class BasicMath {
public static int add(int x, int y) {
int sum;
sum = x + y;
return sum;
}
public static int subtract(int x, int y) {
int diff;
diff = x - y;
return diff;
}
}
Test Class
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
public class BasicMathTest {
@Test
public void testAdd() {
int expect;
int actual;
expect = 15;
actual = BasicMath.add(7, 8);
assertEquals(expect, actual);
}
@Test
public void testSubtract() {
int expect;
int actual;
expect = 2;
actual = BasicMath.subtract(3, 1);
assertEquals(expect, actual);
}
}
Notice in the above examples:
- Test methods are
void, non-static, and have no parameters.- The
@Testlines are annotations that tell JUnit which methods should be run as tests. A class might have other (helper) methods that aren't considered tests. To use@Test, you must import theorg.junit.jupiter.api.Testclass.
- The
- Each test method establishes an expected value and runs the corresponding method to get the actual value.
- In the above example, we expect that 7 + 8 will be 15, and that 3 - 1 will be 2.
The actual values (15 and 2) should be what the methods in
BasicMathreturn.
- In the above example, we expect that 7 + 8 will be 15, and that 3 - 1 will be 2.
The actual values (15 and 2) should be what the methods in
- Each test method uses
assertEquals(or other assert methods provided by JUnit) to verify correctness.- The
assertEqualsmethod belongs to theorg.junit.jupiter.api.Assertionsclass. Theimport staticstatement allows you to call these methods directly, without having to specify theAssertionsclass name.
- The
Tip
Skim the Assertions documentation to see how many methods are available.
Step 1. Getting Started¶
Do the following steps in VS Code:
- Create a
lab05folder under yoursrc/labsfolder. - Create a file named
CarTest.java(underlab05). - Write the
packageandpublic classstatements. - Write a Javadoc comment with your name and date.
- Add the following lines (at the top, underneath the package declaration) to import JUnit:
import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; - Add this line to import your
Carclass from Lab 3:import labs.lab03.Car;Car.javamust be in thelabs/lab03folder for this import to work. If you don't have aCarclass, please download the solution from Canvas.
Step 2. Test the Constructor¶
For the first test, we'll make sure the constructor and getter methods work correctly.
Add the following method to CarTest:
@Test
public void testConstructor() {
}
Add the following code to testConstructor():
- Create two
Carobjects.- Each car should have a different make and year.
- Write three
assertEqualsstatements for each car:getMake()should return the make you chose.getSpeed()should return zero (at this point).getYear()should return the year you chose.
Run the test by clicking the green play () button next to the test method. If the test fails, make whatever changes are necessary in your code.
Info
Tests can also be run from the "Test Explorer" (the large icon) on the left side of VS Code.
Run With Coverage!¶
After the test passes, right-click the green play button (it may now look like a green checkmark with a circle around it ) and select "Run with Coverage".
Info
You can also use the "Test Explorer" to run tests with coverage, by clicking on the third run button next to a test:

Now, open up the code for Car.java.
In Car.java, the constructor and getters should be highlighted in green, meaning those lines were executed during the test.
The other methods should be highlighted in red, meaning they were not executed during the test.

Close the "Test Coverage" window (lower left corner of VS Code, in the Test Explorer) to clear coverage results.
AI: Terminology is Important! ✨
What is coverage and why is it useful? Well, let's ask AI and see if we can get a good summary:
- Open your AI tool of choice (e.g., Microsoft Copilot Chat).
- Ask the following question:
"What is coverage?" - Was that helpful? Probably not!
- This is why we need to be specific and also why we need to know our terminology, so that we can communicate precisely and efficiently!
- With AI, context and specificity are key!
- Try again:
"Explain to an intro Java dev: What is code coverage?"- Don't forget, you can also include:
"Explain concisely:" - Or include:
"Explain like I'm five:"
- Don't forget, you can also include:
- Was that better?
What is Coverage?¶
Here's our explanation:
Think of your code like a 🌳wilderness🌲. Code coverage measures how much of the wilderness you have mapped out and which paths you have taken. If you've only mapped out half of the wilderness, can you guarantee that the other half is safe? Or maybe you did walk through and visit every location, but did you actively eat every single berry 🍒 to verify that it's not poisonous 💀?
Similarly, achieving 100% code coverage does not guarantee that our code is bug-free 🐞. It only tells us if we've adequately explored every part of our code. Just running a method will "cover" it, even if you don't do anything with its output.
Coverage helps you measure how much of your code is actually run when tested. Writing a variety of good test cases should always be your goal.
Step 3. Test Driving the Car¶
Add the following method to CarTest:
@Test
public void testAcceleration() {
}
This test should do the following:
- Create a
Carobject. - Call the
accelerate()method.- Assert that the method returned
"0.0 + 5.0 = 5.0". - Assert that the
Carobject's speed is now 5.
- Assert that the method returned
- Call the
brake()method.- Assert that the
Carobject's speed is now 0.
- Assert that the
Run the test and make sure the test passes.
Then run the test again with coverage.
Some lines of Car.java are green, some are yellow, and some are red.
You still need to test accelerating when speed is 150 and braking when speed is 0.
Add the following method to CarTest:
@Test
public void testSpeedLimits() {
}
This test should do the following:
- Create a
Carobject. - Call the
brake()method.- Assert that the
Carobject's speed is 0.
- Assert that the
- Call the
accelerate()method 30 times, and then:- Assert that the method returned
"145.0 + 5.0 = 150.0". - Assert that the
Carobject's speed is now 150.
- Assert that the method returned
- Call the
accelerate()method one more time.- Assert that the method returned
"150.0 + 0.0 = 150.0". - Assert that the
Carobject's speed is still 150.
- Assert that the method returned
At this point, your tests should have full coverage of the accelerate() method.
AI: Learning Patterns, not Specific Solutions
Sometimes,
- Ask the following:
"Write a Java method with a logic error." - Did the response include an explanation of the error?
- If so, oops! Sometimes different AI models can try to be too helpful.
- Try this instead:
"Write a Java method with a logic error, but don't tell me."
- Now, ask a follow-up:
"What kinds of test cases would be helpful?"- We include the word "kinds" so that we're able to learn general approaches to unit testing.
- Read through and think about why each kind of test is useful.
- Ask follow-up questions if you need more info: `"Why would testing _____ be useful?"
- Finally, let's summarize:
"In general, what kinds of test cases should I think about?" - You should always test at least the following (try asking AI about any of these):
Step 4. equals() and toString()¶
Add the following test methods to CarTest:
@Test
public void testEquals() {
}
@Test
public void testString() {
}
The first method should do the following:
- Create four
Carobjects.car1andcar2should have the samemakeandyear.car3should have a differentmakebut the sameyearascar1(andcar2).car4should have the samemakebut a differentyearascar1(andcar2).
- Compare four
Carobjects.car1should "equals"car2.car1should not "equals" the other two cars.
- Compare
car1with a different type of object (in this case, aString):assertFalse(car1.equals("car1"));
Calling the equals() method four times is necessary to cover all the branches.
In contrast, the toString() method does not have any branches.
So only one method call is necessary to get full coverage of toString():
- Create a
Carobject. - Call the
toString()method, and assert that the result is correct.
Step 5. Final Test and Submit¶
Click the green play () button next to public class CarTest.
All five tests should pass, and you should have 100% code coverage of the Car class.
Submit only CarTest.java to Gradescope.
Your code will compile and run with the Car.java sample solution.
Points will be allocated as follows:
| Criterion | Points | Details |
|---|---|---|
| Compile | 0 pts | Success Required |
| CompileOfficialTests | 0 pts | Success Required |
| Style | 0 pts | Success Required |
| SelfTests | 2 pts | Partial Credit Possible |
| Coverage | 4 pts | Partial Credit Possible |
| OfficialTests | 4 pts | Partial Credit Possible |