Unit Testing Lab
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:
- Replace the
fail
method call with an assertion, for example: - 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.
Testing for Exceptions
There are several ways to test for thrown exceptions in JUnit. The easiest way (see below and also the assertThrows example on the CS wiki ) requires a language feature we haven’t covered yet, so we recommend you use the first example in the CS wiki which involves your junit test catching the expected error, and using junit’s fail method after the line you expected to throw.
The “canonical ” way is to use assertThrows
:
The ->
operator defines a lambda expression.
In programming, a "lambda" is a block of code that can be assigned to a variable.
Lambdas allow you to write unnamed methods on the fly and pass them as parameters to other methods.
In the above example, we define an unnamed method that takes no paramters ()
and that invokes weatherAdvice
.
Make sure you can answer the following questions:
- Why do you need to define a lambda expression? Why can't you just pass the result of
WeatherUtils.weatherAdvice
toassertThrows
? - 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
Note
There’s a little incompatibility problem with our Gradescope setup right now. If you make the following tiny changes, you should still be able to run locally with the expected test and coverage analysis results, but also actually get points from Gradescope:
- Ensure that your test class is
public
(i.e.public class WeatherUtilsTest
…) - Ensure that your test methods are
public
(i.epublic void testDefaultConstructor() {...
) - Change the imports in your test class. Your test class hopefully has only these import statements:
but in order to workaround a little Gradescope issue we’re having, please replace these with the following (this should not cause the tests to stop passing or compiling locally [on your machine], and neither should this have an effect on your coverage report):
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.