| CPSC 329 | Software Development | Fall 2026 |
This lab introduces some tools for testing and debugging, including JUnit, the Eclipse debugger, assertions, and Java's built-in logging. It also reinforces incorporating version control into the regular workflow.
Labs are for learning and practice — you may help and be helped by others when it comes to figuring out what to do, but in the end you need to understand and be able to do the things for yourself.
You may not use AI to figure out what to do or to do the task for you.
due Fri 9/11 at the start of class
To hand in your work:
Copy your entire ~/cs329/dev/lab2 directory into your handin directory. Make sure you end up with a lab2 directory inside your handin directory that contains the src directory (with your code) — you shouldn't have a bunch of stuff loose in the top level of your handin directory or multiple levels deep of lab2 folders.
In Eclipse, take a screenshot of the History view that shows your two commits (with the commit comment, author, and timestamp info) and save it with the name "commit.png" in the top level of your handed-in lab2 directory (the one in your handin folder).
You've been provided with a partial implementation of a Klondike Solitaire game, following the class organization discussed in class.
In this part you'll practice a tests-early approach to development and learn how to use JUnit to implement and run tests.
Start with two core methods of TableauColumn: the constructor and isEmpty:
Implement the constructor and isEmpty so they work as specified in the method header comments. You'll need to add the header for the constructor; its job is to create a new empty column (with no cards). (Move the initialization of cards_ that is already done into the body of the constructor — this is a little nicer style, though both versions are legal and the functionality is the same.)
There are three behaviors to test: that the constructor creates an empty column, that isEmpty() returns true when there are no cards, and that isEmpty() returns false when there are cards.
Both isEmpty() and the constructor are high-risk if they are wrong (everything depends on the constructor and many things, including many tests themselves, depend on isEmpty but low-risk for actual bugs since their bodies are very short and simple. We'll review isEmpty() directly but write a test for the constructor to help catch potential initialization errors.
Review the body of isEmpty() and verify that it returns true when there are no cards and false when there are cards.
The following test covers the constructor's behavior:
| test name | description | starting state | input | expected result |
|---|---|---|---|---|
| newColumnIsEmpty | a freshly constructed column has no cards | isEmpty() returns true |
Tests should be implemented separately from the code being tested — tests aren't part of the final production version, and testing shouldn't modify the code being tested. All of the tests for a given class typically go into a single class, which JUnit confusingly calls a "test case" (even though there are actually many different cases being tested).
Set up a new test case:
You should now see the new class TableauColumnTest with a placeholder for the constructor test.
The JUnit dashboard will pop up showing the status of each test case it completes.
To implement a test, replace the default fail("not yet implemented") with a body that does the three basic steps of a test: set up the starting state, do the operation being tested, and compared what actually happened to the expected result.
TableauColumn column = new TableauColumn();
assertTrue(column.isEmpty());
testTableauColumn should now pass — it if doesn't, fix the bug (in the constructor) and run the test again to ensure it passes.
The following tests cover place(Card)'s behavior — the typical operation of adding a card to a non-empty column and the special case of adding a card to an empty column:
| test name | description | starting state | input | expected result |
|---|---|---|---|---|
| placeOnNonEmpty | placing a card adds it to the top of the column | column with (just) a face-up red 8 | place black 7 | black 7 is now on top; the column contains two cards |
| placeOnEmpty | placing a card on an empty column | empty column | place red king | red king is now on top; the column contains one card |
For each test:
@Test
void testXYZ () {
}
The @Test annotation is what identifies this method as
a JUnit test. The return type must be void and test
methods take no parameters. It is conventional to name the test
method starting with "test" though that is no longer required by
JUnit. The rest of the method's name should capture what the
test is testing — use the descriptive name assigned when
identifying the tests.
Then:
The following tests cover canPlace(Card)'s behavior — the typical operation of adding a card to a non-empty column and the special case of adding a card to an empty column:
| test name | description | starting state | input | expected result |
|---|---|---|---|---|
| canPlaceLegalOnTop | check a legal card on a non-empty column | column with (just) a face-up red 8 | check black 7 | returns true |
| canPlaceLegalOnEmpty | check a legal card on an empty column | empty column | check red king | returns true |
| canPlaceIllegalOnTopSameColor | check an illegal card (color) on a non-empty column | column with (just) a face-up red 8 | check red 7 | returns false |
| canPlaceIllegalOnTopWrongRank | check an illegal card (rank) on a non-empty column | column with (just) a face-up red 8 | check black 5 | returns false |
| canPlaceIllegalOnEmpty | check an illegal card on an empty column | empty column | check black 7 | returns false |
Implement and test canPlace(Card):
In this part you will use the Eclipse debugger to locate bugs in pickUp()/pickUp(n). A set of tests for these methods has been implemented for you.
Debug: