Unit Testing Lab
forEquals
methods than the Test class provides? Wanted a better summary of successes and failures? Wanted to measure how much of an implementation I’m testing?Categories:
6 minute read
Unit Testing and Code Coverage
Severe Weather Policy
The goal of this lab is to gain experience writing JUnit tests and using code coverage analysis.
Background
Suppose that JMU had the following severe weather cancellation policy: (here's the real one)
JMU (FAKE) SEVERE WEATHER POLICY
If any of the following conditions are met, campus will be closed:
- More than 6.0 inches of rain are predicted within the next 24 hours.
- Wind speeds greater than 70.0 mph are predicted within the next 24 hours.
- It is predicted that there will be more than 4.0 inches of rain and wind speeds will be above 45.0 mph.
If none of the above conditions are met, the University may still issue a warning and encourage individuals to avoid non-essential outdoor activities. A warning will be issued if any of the following conditions are met:
- Predicted wind speeds are above 45.0 mph.
- Predicted precipitation is above 4.0 inches.
The following Java class has been developed as part of a JMU Decision Support system:
The weatherAdvice
method provides an implementation
of the cancellation policy above. Your job is to develop a set of
JUnit tests to confirm that this method is implemented correctly.
Your testing must also confirm that the correct exception is thrown
when the method receives invalid input.
Part 1 - 100% Coverage
Creating a JUnit Test Class
Once you have set up an Eclipse project containing
WeatherUtils.java
, create a stub JUnit test class:
- Right click the
WeatherUtils.java
in thePackage Explorer
and selectNew
->JUnit Test Case
. Make sureNew JUnit Jupiter test
is selected. Click theNext
button, check the box forWeatherUtils
, and clickFinish
(don’t worry if you see a warning against using the default package). This should prompt you to add Junit 5 to the build path and create a new file namedWeatherUtilsTest.java
. - Write Javadoc comments so that Checkstyle is happy. For example:
import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; /** * Tests the WeatherUtils class. * * @author * @version */ public class WeatherUtilsTest { /** * Tests the weatherAdvice method. */ @Test public void testWeatherAdvice() { fail("Not yet implemented"); } }
- Replace the
fail
method call with an assertion, for example:assertEquals("CANCEL", WeatherUtils.weatherAdvice(70.1, 0.0));
- Run the JUnit test by clicking the green Run button on the toolbar. If you see a green bar on the left, your test passed; if you see a red bar, your test failed.
- You can now run coverage analysis against your program by clicking the button to the right of
Run
(this button should sayCoverage
in the tooltip when you hover your mouse cursor over it). This should color-code your Java files to indicate which lines are being exercised by your tests. You should also see a new tab labeledCoverage
that will display coverage percentages for each file.- Note: Consider adjusting your theme away from
Dark
themes if the highlighting that indicates your test coverage makes the file nearly illegible (for some of the dark themes).
- Note: Consider adjusting your theme away from
- You can select which coverage metric to display (Line Counters, Branch Counters, etc.)
by clicking on the
View Menu
(3 vertical dots icon) in the far right of theCoverage
window.
Writing Additional Tests
Complete WeatherUtilsTest.java
by writing an
appropriate set of test methods. You should be able to
check your code coverage from within Eclipse. Keep working until your tests
cover 100% of the method.
Once you are confident that your unit tests are sufficient, demonstrate them and your coverage to the instructor or TA. Your goal is to achieve 100% method, statement, and branch coverage.
Note: In order to cover the public class WeatherUtils
line, you will need to construct a (useless) WeatherUtils object.
The following example "covers" the default constructor.
Notice that there is nothing to assert.
/**
* Tests the constructor (for 100% coverage).
*/
@Test
public void testDefaultConstructor() {
new WeatherUtils();
}
Testing for Exceptions
There are several ways to test for thrown exceptions in JUnit. For now, we propose you use the 1st method detailed in the JMU CS Wiki (pasted below for your convenience) , which involves your junit test catch
ing the expected error, and using junit’s fail method after the line you expected to throw
.
/**
* Test that the constructor validates properly.
* NOTE: the Atom class is just an example, the idea here is that the Atom
* class's constructor should throw an IllegalArgumentException if the
* atomic number or atomic weight are invalid (e.g. negative).
*/
@Test
public void constructor_IllegalArguments() {
try {
new Atom("O", -8, -16);
// Shouldn’t get here
fail("Constructor should have thrown an IllegalArgumentException");
} catch (IllegalArgumentException iae) {
// The exception was thrown as expected
}
}
Make sure you can answer the following questions:
- How do you write a test that confirms that the implementation throws an exception when specified?
- How do you write a test that confirms that the implementation does not throw an exception when specified not to?
- What does code coverage look like when exceptions are thrown? Is there any way to get 100% coverage for a test class that has exceptions?
Submission
Upload your completed WeatherUtilsTest.java
file to Gradescope.
To be graded, your code must (1) pass Checkstyle and (2) pass JUnit. If any of your tests fail, you will need to revise and resubmit.
Part 2 - Boundary Cases (Optional)
100% coverage doesn’t necessarily mean that your tests
are good enough. High-quality tests should be able to uncover
errors in the code that is being tested. The next step is to run your
tests against an implementation that is known to contain errors. If
your tests are effective, they should indicate that there is a problem
with this code. For this part of the lab, you will run your tests
against a pre-compiled .class
file that we have intentionally coded to
contain at least one error.
- Create a new
Java Project
. - Copy your completed
WeatherUtilsTest.java
file into the new project, and make sure that JUnit is on the classpath (right-click the project, go down toBuild Path
, and clickAdd Libraries
…JUnit
…(ensure JUnit5 is selected)…Finish
). - Download the file WeatherUtils.class to anywhere but your Eclipse project folder
- Go to
Project
/Properties
and click onJava Build Path
. - Click on
Libraries
->Classpath
->Add External Class Folder
. Navigate to the folder where you downloaded the WeatherUtils.class file. - Press “Okay”. You will see a new section in the Package Explorer window called “Referenced Libraries” and you should see your new folder beneath it.
- Execute the tests to confirm that at least one test fails.