CSE 373, Summer 2019: Project 0 - Introduction to Testing

Summary

Due Wednesday, July 3 at 11:59pm.

Submit by pushing your code to GitLab, as described in the final part of the project. If you intend to submit late, fill out this late submission form when you submit.

You will be writing tests in the following file:

  • src/test/java/junitpractice/TestBuggyDeleteElement.java

You can run your tests using the following file:

  • src/test/java/junitpractice/CheckTestsDetectBuggyImplementation.java

Additionally, it might be helpful to examine the following files, which may help you during test writing:

  • src/main/java/junitpractice/BuggyIntList.java
  • src/test/java/datastructures/TestLinkedIntListProblems.java (example tests that you can follow)

Part d.a: Writing Tests

In CSE 143, we learned about the difference between an implementor and a client/user of a class. It turns out that as an implementor, one of the best ways of making sure your code behaves correctly is by pretending that you are a client and coming up with different scenarios of using your class. This is the basic idea behind unit testing, in which one basically aggressively tests against particular units (e.g. class methods) using different cases.

For this part, we are asking you to work with a BuggyIntList class and to write test cases for one unit: the method deleteElement(...). The BuggyIntList class behaves exactly like the LinkedIntList class, just with the extra buggy method that you are testing. A description of what this method is expected to do and what exceptions it might throw can be found in BuggyIntList.java where it is implemented.

We have a fairly comprehensive JUnit Guide that provides a good starting point for you to learn about its syntax. Although for this part, you are primarily graded on whether if you can reasonably complete the tests that we have explicitly asked you to write, it is also good to start thinking about the different cases that a good test suite should cover.


Task: Implement testDeleteFromMiddle and testDeleteFromFront

In these two tests we ask you to cover two cases: a middle case and a front case. For these kinds of tests, you usually have to initialize a list yourself with some sample data. You can see if the method behaves correctly by calling the method on your list, and later examining the list again to see if it is in an expected state (i.e. if its contents are what the user would reasonably expect after using the method).

Don't worry if you can't get testDeleteFromFront to pass - the implementation that we gave you doesn't handle certain cases correctly, so the test you write should actually fail if you run it directly. Instead of running it directly, you should run your tests using the instructions in Part d.b later.

Hint: get(..) and toString() are two methods that allow you to check the contents of the list.

Hint: you'll likely want to use assertThat and is to check the state is what you expect. If so, make sure these methods are imported at the top as

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.is;
Reminder: see the JUnit Guide's examples to see these in context.


Task: Implement testThrowsIllegalArgumentException

In the method comments for deleteElement(..), the implementation tells you that an IllegalArgumentException will be thrown if the given element is not found in the list. You should write a test to make sure that the exception is indeed correctly thrown when the prescribed conditions are (not) met. The JUnit Guide has a section on testing exceptions that might be helpful for this task.

As it stands, our buggy implementation actually does not throw the correct exception, and so this test that you will write is also expected to fail.

Hint: you'll likely want to use assertThrows to check the correct exception is thrown. If so, make sure that it's imported at the top as

import static org.junit.jupiter.api.Assertions.assertThrows;

Part d.b: Running Tests

It might be a little weird to see your tests fail, even if you know for sure that your tests are expecting the correct results. As you can tell, since the implementation that we provided is intentionally buggy, it is expected that not all the tests you write will pass as expected.

Instead, when running your tests, you should use the CheckTestsDetectBuggyImplementation class, which lives in the same directory as your tests. This class actually uses all the tests that you wrote earlier, but it explicitly tells JUnit for which tests that we expect you to throw an AssertionError. This way your tests will be marked ✔ PASSED if they are expected to fail.