HW4: Krypton¶
Learning Objectives¶
This homework assignment is designed to help you understand unit testing and debugging. This includes an understanding of black-box/closed-box testing, white-box/open-box testing (and the JUnit framework), test coverage (and the EclEmma/JaCoCo framework), the debugging process, and instrumentation techniques.
Motivation¶
Thus far, your testing has been both haphazard (i.e., you have not thought carefully about how to test your code to ensure that it is correct) and ad hoc (i.e., you have not used tools that make it easier to test your code). In addition, you have probably relied to heavily on the submission system to test your code (because we have not penalized you for "excess" submissions). Going forward, you will have to do a much better job of testing your code, and this assignment is designed to help you do so.
To help motivate some of the aspects of this assignment, it is useful to briefly return to the previous assignment, test it using JUnit, consider some issues related to coverage, and think about the implications for the current assignment.
To that end, download the following file (that contains JUnit tests for the previous assignment):
BattlemintonWhiteBoxTests.java
add the file to the directory/folder hw3
, and answer the
following questions. (Note: You do not have to submit the answers to
these questions.)
Questions About The Importance of Test Coverage¶
-
Run
BattlemintonWhiteBoxTests
(i.e. click on the ▷ icon to the left of the class declaration forBattlemintonWhiteBoxTests
). Does your code fail any of the tests? In other words, are any tests highlighted? (It should pass all of the tests if your solution is correct or you are using the solution that was provided to you.) -
Check how these tests covered
BattlemintonEvent.java
. There are two ways to do this. One way is to openBattlemintonEvent.java
and select "Watch" at the very bottom of the IDE. The other way is to type Ctrl+P (or Cmd+P) the word "task" (no quotes), a space and select "Create coverage report", type the word "hw3" (no quotes), press Enter, type Ctrl+P (or Cmd+P), the word "task" (no quotes), a space, and select "Show coverage report". (Note: The second approach takes a few seconds more but provides more detailed information.) Do the tests completely cover the statements and branches inBattlemintonEvent.java
? -
Comment-out the last statement in the
testBattle()
method (i.e., the call toincreaseWestScore()
in theBattlemintonWhiteBoxTests
class), runBattlemintonWhiteBoxTests
, and use the coverage tool. What is now true of the branch coverage of thepoints()
method? Why? -
Why is it important to have 100% statement coverage and 100% branch coverage?
Before you proceed, undo the change you made to the BattlemintonWhiteBoxTests
class.
Questions About The Need For More Than Just Coverage¶
- Temporarily, change the
increaseWestScore()
method in theBattle
class so that it increases thescoreWest
attribute by 50, and runBattlemintonWhiteBoxTests
using the coverage tool. Despite the fact that theBattle
class now contains a defect, does it pass all of the tests? Is theBattle
class completely covered? - Does the
BattlemintonWhiteBoxTests
class test theincreaseWestScore()
method or does it just execute it? Does the coverage tool indicate that statements/branches have been tested, or just that they've been executed? - Add the statement
assertEquals("Deadpool 10, Wolverine 0", battle.toString(), "increaseWestScore()");
to the end of thetestBattleMethod()
and rerun the tests. Does the code now pass all of the tests? - Correct the defect in the
Battle
class and rerun the tests. Does the code now pass all of the tests? - In the
BattlemintonEvent
enum, temporarily change the speed of thePROPELLER
to900
rather than90
, and launchBattlemintonWhiteBoxTests
using the coverage tool. Is theBattlemintonEvent
enum completely covered? Despite the fact that theBattlemintonEvent
enum now contains a defect, does it pass all of the tests? Why? - Is it enough to just cover code when writing unit tests?
- What are the implications of your answer to the previous questions? In other words, what must you be sure to do when you are required to write and submit unit tests for future assignments?
Before you proceed, undo the change you made to the BattlemintonEvent
class.
Questions About Unit Testing¶
- Which testing process was easier to use, the one that you used for the previous assignment or the one you just used? (Focus just on the process and ignore whether you had to write the tests or not.) Why?
- If you become a professional programmer, there will not be a submission system for you to rely on. Instead, what must you do to ensure that your code is correct? With that in mind, what must you now start doing this semester?
Overview¶
The (fictitious) company SuperCypher has written a Krypton
class (from the
Greek word κρυπτός which means "the hidden one") that can be
used to encrypt and decrypt messages using a secret key phrase. They
have also written a suite of white box tests that covers all
statements and branches in this class. They have come to you to do
additional testing of the Krypton
class and to debug it if
necessary. (Hint: It will be necessary to debug the Krypton
class. In other words, it does contain errors.)
Definitions¶
- Black-Box/Closed-Box Tests
- Tests that is based on specifications, rather than the actual implementation of the code.
- White Box/Open-Box Testing
- Tests that are based on the actual implementation of the code.
- Heuristic
- An acceptable approximation.
- Rule of Thumb
- A guideline that is informed by experience.
- Random Tests
- Tests with inputs that were chosen at random by the tester. As with any test, it includes the expected output.
Existing Classes¶
SuperCypher has provided you with an implementation of the Krypton
class and a white box test suite (written in Java using the JUnit
framework) that covers all statements and branches in the
Krypton
class.
The specifications for the Krypton
class are summarized in the
following UML class diagram:
(As in the past, the package information has not been included in this UML class diagram.)
A Krypton
object uses a key phrase (containing all of the characters
in the alphabet) to encrypt and decrypt messages. The remaining
specifications are included in the comments for the class. The
comments accurately describe what the code must do, not what it
actually does (since the code may contain errors).
Your Tasks¶
You must test and debug the Krypton
class that you have been
given. The end result must be a correct version of the Krypton
class with every change you make documented in the
comments. Specifically, you must:
- Create a public class named
HeuristicTests.java
(in thehw4
package) that contains JUnit tests based on "rules of thumb" about the inputs for all of the methods in theKrypton
class. - Create a public class named
RandomTests.java
(in thehw4
package) that contains JUnit tests based on random inputs for all of the methods in theKrypton
class.
Note
The RandomTests
class should not use the Random
class in
Java. You should construct inputs randomly by hand (along with the
expected outputs) before you create the JUnit code. Nothing random
should happen at run-time.
Note
Your test classes must be declared to be public
. Do not omit the
visibility/accessibility modifier.
Both classes must eventually have at least five methods. These methods
must be preceded by the JUnit @Test
annotation and must have names
that begin with constructIncidenceArrayTest
, checkForMissingCharactersTest
, indexOfOccurrenceTest
, decryptTest
, and
encryptTest
. (They can have suffixes like decryptTest1()
,
decryptTest2()
, etc. if you would like to have more than five in
total). You should add these methods one at a time, running all of the
tests each time you do so.
When the actual output of one of your tests does not equal the expected output you should understand the symptom and the trigger condition (i.e., stabilize the fault). Then, localize the fault, correct the fault (documenting the correction in the source code), and verify the correction.
Recommended Process¶
It is strongly recommended that you test and debug one method at a time. One possible order is:
- Test the
checkForMissingCharacters()
method. (It isstatic
and independent of any other methods.) - Test the
constructIncidenceArray()
method. (It isstatic
and independent of any other methods.) - Test the
indexOfOccurrence()
method. (It isstatic
and independent of any other methods.) - Test the constructor. Make sure you have test cases for when it should and ahouldn't throw an exception. (It must be correct before you can test
encrypt()
anddecrypt()
.) - Test the
encrypt()
method. - Test the
decrypt()
method.
Some Likely Rules of Thumb¶
There are a variety of rules of thumb that people use when testing code.
The constructIncidenceArray()
and checkForMissingCharacters()
Methods¶
These methods are passed a String
that is supposed to contain some or all
of the characters in ALPHABET
. Some obvious parameters to pass
include:
- A
String
that is missing one letter - A
String
that is identical toALPHABET
- A
String
that is identical toALPHABET
with the characters in reverse - A
String
that is missing the first letter (i.e., the' '
character) - A
String
that is missing the last letter in lower-case (i.e., the'z'
character) - A
String
that is missing the last letter in upper case (i.e., the'Z'
character) - A
String
that contains no letters
You should be able to think of other rules-of-thumb that might be
likely to uncover defects in a method that is passed a String
.
The indexOfOccurrence()
Method¶
This method returns the index of the \(n^{\text{th}}\) occurrence of a char
in a
String
. So, some obvious parameters to pass include:
- Positive values for
n
- An
n
of0
- Negative values for
n
- Characters that don't appear in the
String
- Characters that appear once in the
String
- Characters that appear multiple times in the
String
The Explicit Value Constructor¶
The explicit value constructor is passed a String
and uses the
checkForMissingCharacters()
and constructIncidenceArray()
methods.
So, the rules of thumb should be obvious.
The encrypt()
Method¶
The encrypt()
method is passed a String
that contains some or all
of the characters in ALPHABET
. Some obvious parameters to pass
include:
- A
String
that contains all of the letters inALPHABET
- A
String
that is identical toALPHABET
- A
String
that is identical toALPHABET
with the characters in reverse - An upper-case
String
- A lower-case
String
- A mixed-case
String
- A short
String
- A long
String
that contains some letters more than once - A
String
that contains achar
that isn't inALPHABET
- A
String
that contains multiplechar
s that aren't inALPHABET
You should be able to think of other rules-of-thumb that might be
likely to uncover defects in a method that is passed a String
.
The decrypt()
Method¶
The decrypt()
method is passed an int[]
that contains the indexes
of some or all of the characters in ALPHABET
. Some obvious
parameters to pass include:
- An array with 1 element
- A long array
- An array in which all of the elements are the same
- A array sorted in ascending order
- A array sorted in descending order
- An array containing negative values
- An array containing zeroes
You should be able to think of other rules-of-thumb that might be
likely to uncover defects in a method that is passed an int[]
.
Submission¶
You must submit (using Gradescope) a .zip
file (named hw4.zip
) that contains the directory/folder hw4
at the top level. It must include:
- Your corrected implementation of the
Krypton
class (with appropriate comments). - Your
RandomTests.java
andHeuristicTests.java
.
Because you should not be using Gradescope to verify your code (i.e., because you must be able to test and debug code without the help of a submission system), you will not be given hints about failed tests.
Note that your test classes do not need to comply with the style guide.
Grading¶
Your code will first be graded by Gradescope and then by the Professor. The grade you receive from Gradescope is the maximum grade that you can receive on the assignment
Gradescope Grading¶
Your code must compile (in Gradescope, this will be indicated in the section on "Does your code compile?") and all class names and method signatures must comply with the specifications (in Gradescope, this will be indicated in the section on "Do your class names, method signatures, etc. comply with the specifications?") for you to receive any points on this assignment. Gradescope will then grade your submission as follows:
Criterion | Points | Details |
---|---|---|
Conformance to the Style Guide (Style) | 0 points | (Success Required) |
Passing Your Tests (SelfTests) | 40 points | (All or Nothing; Success Required) |
Correctness (Official Tests) | 60 points | (Partial Credit Possible) |
As mentioned above, you will not be given any hints if your code does not pass the official tests. Your job on this assignment is to test (and debug) the code you were given, not to use Gradescope to test it.
Also, since you should now be able to use the development tools on your local machine, you will not be given any hints about style defects.
Manual Grading¶
After the due date, the Professor may manually review your code. At this time, points may be deducted if you submit too few tests.
Relevant Programming Patterns¶
An understanding of the following programming patterns will help you complete this assignment:
Questions to Think About¶
You don't have to submit your answers to these questions, but you should try to answer them because they will help you determine whether or not you understand some of the important concepts covered in this assignment.
- The white-box tests completely covered the code. How could it still contain defects?
- Why are both heuristic tests (based on rules of thumb) and random tests necessary (in addition to white-box tests)?
- The
encrypt()
anddecrypt()
methods can be tested in tandem by using the output of the first as the input to the second. Why is it important to test them independently? - As you know, applications written in Java must have a main class that contains a method with the signature
public static void main(String[])
that is called the entry point of the application. When you write JUnit tests, you do not need to create such a class. What must the JUnit framework be providing behind the scenes? - How does your answer to the previous question make it a little more convenient to use JUnit than the
Test
class you have used on previous assignments? - Why can the
checkForMissingCharacters()
,constructIncidenceArray()
, andindexOfOccurrence()
methods be static? - Similarly, why can't the
decrypt()
andencypt()
methods be static?