CompSci 307
Fall 2021
Software Design and Implementation

Lab Coding Exercise: Unit Testing

As you program larger, more complex, projects it is vitally important that you have confidence the most basic aspects of your code works correctly by automatically testing it — meaning the tests can be run as often as needed to test the entire program, not just the most recent code. JUnit, the most common automated framework for Java, focuses on testing individual methods and has inspired similar frameworks in every other modern language.

Submission

There will not be an official submission for this exercise, instead show your progress to a UTA during lab when you think you are finished to get feedback.

Resources

Specifications

In pairs, write new tests for (and, as needed, debug) the following programs using your forked version of the lab_unitest project. Make sure each person gets to write a variety of tests and that every few tests, you add, commit, and push your changes.

Write as many tests as you can think of to verify the code and, in the process, find any bugs or other issues with the code using these steps:

  1. Add a new JUnit test to verify whether or not the code works as intended
  2. Run all the tests to verify they all continue to pass (both the old and new tests show green)
  3. If you find a bug: (i.e., a test fails by showing red)
    1. Comment out the buggy code
    2. Write a comment telling the cause of the error
    3. Write the correct code that fixes the error
  4. Again, run all the tests to verify they all continue to pass

Each test should be:

Consider the following questions as you try to come up with useful scenarios to test the program:

  1. What would be the most simple data you could make to test the program does something reasonable?
  2. What "special cases" does the program check for (or not)?
  3. What "categories" does program behavior naturally fall into? What data shows the boundaries of those categories?
  4. What "bad" data values you can think of and what checks would you add to the code to make it more robust from failure or a nonsensical answer?

Resist the urge to simply create a lot of "random" tests (i.e., the shotgun approach) and focus on making each test as useful as possible (i.e., the sniper approach).

You can also check which lines of code are not executed by your tests.

Buggy Programs

Container

This is a very buggy example in a few lines of code, even though the given tests all pass!

There are at least 5 errors in the code. Update the JUnit code by writing only new tests to verify the existing bugs. How you fix the bugs is entirely up to you, but fix one at a time to emphasize thinking about different strategies for what to test (e.g., do not simply change the implementation to an ArrayList and fix all the compilation errors since that will likely fix most of the bugs without you having to think about tests for each).

Bowling

This program simulates scoring a game of bowling, which consists of 10 connected frames to provide bonuses for knocking down all 10 pins within the frame. Thus, the maximum possible score you can achieve is 300, by rolling twelve strikes in a row, instead of 120, the simple value of 10 pins scored directly for 12 rolls.

There is at least 1 error in the code. Update the JUnit code by writing only new tests to verify the existing bug(s).

Here is an interactive scorer to help you develop and verify your test cases.

Add Inheritance

There is another scoring mechanism used for World Tournament matches that is both simpler to implement and demonstrably more entertaining. If you want more practice, create a scoring abstraction (superclass) with two concrete implementations (subclasses): traditional and world tournament rules. Then update your tests to show that each works separately (i.e., where a frame's score would be different) and without regard to implementation (i.e., where a frame's score would be the same).

Pair Programming

Labs in this course expect you to work either in pairs or with your project team.

Professionally, this practice is called Pair Programming: working closely with another programmer, sharing a "single computer" that we will simulate using a shared computer, shared repository or use IntelliJ's new Code With Me feature. To ensure both people do some coding, you will switch which person actually writes the code every 10-15 minutes (in industry this switch happens only 1-3 times per day). The person who is not actively coding can be advising, suggesting better names, looking up documentation, or searching the Internet for solutions to small problems you are likely to face, but not multi-tasking (i.e., doing their own work or socializing).