In coding classrooms and professional computer science environments, unit testing is used to make sure all the methods and constructors in a program work as they are supposed to. A unit test is a helpful tool – it’s a lot easier to find a bug in a massive program if you know what part of the program is misbehaving. Thus, a well-designed suite of unit tests is critical in many environments. Some classes and employers even require coders to write their own unit tests to prove they’ve done their due diligence when considering the requirements and possible failure points of their problem.
JUnit is a code library used for testing Java programs. In this blog post, I hope to give you a brief overview of what JUnit is, how it works, and how you can best make use of it in your coding class. I write this primarily for teachers who would like to write some automated feedback to give to their students. However, it should be helpful for students who would like to understand exactly how their class’s autograder works and how to read its feedback.
Consider a method where a student has to find the maximum value in an array of integers. For simplicity’s sake, we’ll ignore arrays that are zero-length or null. A student’s solution might look like this:
A teacher’s unit tests for this problem may look like these:
The student’s code fails the first test (it returns 0) but passes the second. In Coding Rooms, the student would be presented with an error message showing the array that caused the failure condition, the expected value, and the actual value that was returned. Had they failed the second test, they wouldn’t have seen the array in the error message because no failure message exists in that test (more on that later). Passing a test just results in a green checkmark with little to no other feedback.
JUnit tests are used to test individual “units” of a program - most commonly a class or an individual method. A test will typically run a method or constructor then verify that the program is behaving as expected using assert statements. If any assert fails, the entire test fails. I use asserts to verify that methods are returning the correct value or that constructors are properly initializing instance variables.
The full documentation for JUnit asserts can be found here, but here are the methods I find most useful in my Java courses:
Note that both of the equality asserts can take any primitive or Object (so long as the primitives are the same type or the Objects are comparable). For all the asserts, String message is an optional message to be given to the programmer in the event their code does not pass the test. I like to include a failure message that gives a hint or provides the test data so that the student knows what failed. Without a tailored failure message, they might be helpless to figure out what to do with feedback that looks like “Expected <true> but was <false>”.
Each test can have multiple assert statements – mine usually do, just to make sure I am testing as many cases as I can come up with. It also helps to prevent students from looking at the expected values and tailoring their programs to only work with the provided test cases!
Typical Junit tests are not used to check the way a student goes about solving a problem or code style. So even if the code passes all the unit tests, it may not meet all the requirements for a great grade. Teachers will still have to check to make sure the code uses good style and conventions.
I recently had a student write bubble sort on an assignment about quicksort! The code passed the unit tests because I only tested to see if arrays became sorted after calling the sort method, but the implementation was incorrect. For problems like this, it is difficult to write an automated test to verify that a problem was solved in a particular way. Even timing a method to see if it behaves like a particular time complexity is not reliable on most systems.
Many students, especially those who are still getting used to the difference between returning and printing a value, might try to include extraneous print statements in their code. Those can seriously slow down the runtime of a test, especially if the test is run many times. Tests that take too long to run might time out and produce no helpful output.
Coding Rooms does allow for easy testing of a program’s printed output - just select “Input/Output Comparison” instead of “Unit Test” when adding a test in the Test Bench.
JUnit is a powerful tool in coding classrooms. Teachers can automate their feedback, streamlining their teaching experience and letting students better figure out how to improve their programs. Although it has its limitations, I consider JUnit to be a core part of teaching any of my Java sections. It’s worth learning and trying out on some assignments if you are not already using it. Coding Rooms makes the automated testing process easy – I highly recommend you give it a try!
For more information about autograding with unit tests, check out: https://auto-grade.joemazzone.net/
Computer Science Teacher and Robotics Coach
Avon Old Farms School
Avon, Connecticut (just outside of Hartford)