Target Audience: this guided project is geared toward the beginning Java programmer who has some experience with using Eclipse, inheritance, and running test cases.
An important practice for ensuring that software works correctly is to test it and run other verification and validation tools to minimize programmer mistakes. Since no one practice can identify all faults, developers work with a suite of practices and tools to help find problems in their code.
One practice that developers frequently use is test driven development (TDD). In TDD, a developer starts by writing tests, then uses those test to drive forward development of the implementation. By writing system and unit tests before writing the implementation, a developer can better understand the context of the problem and the area of the solution.
After completing this Guided Project, students will be able to…
Use test driven development to solve a software problem.
Revise and write system level tests.
Write unit level tests.
Use coverage metrics to identify and cover unexecuted paths.
Use software engineering best practices like test-driven development, code coverage, static analysis, version control, continuous integration, and documentation with supporting tooling to implement and test object-oriented systems.
WolfScheduler System
You will implement and test the remainder of the WolfScheduler system, with an emphasis on conflict identification and resolution. The WolfScheduler system enables a student to determine which course schedule may be best for them in an upcoming semester.
You will develop the WolfScheduler project over the course of the three guided projects. The WolfScheduler requirements describe the fully implemented system. Guided Project 3 will focus the [Schedule Conflict] subflows of Use Cases 4 & 6 around time (with an assumption of no regression in functionality on the already implemented requirements):
As part of earlier Guided Projects, you were provided with a suite of unit and system tests. You will now be adding new tests for the time conflict functionality. You may have to update the provided tests. Some may fail due to the addition of the new time conflict functionality.
Double check your implementation to ensure that you're meeting all expectations.
Total Estimated Time*:
6 hours 55 minutes
* Times are only estimates. It may take you less time or more time depending on how much debugging effort is required for your specific implementation. Use the relative numbers to help guide you: for example, a task labeled as "5 minutes" should not require as much effort as a task labeled "20 minutes", regardless of how much time it takes to actually complete the task.
Guided Task: Guided Project 3 GitHub Repository
You will be assigned a separate GitHub repository for each Guided Project and each major Project. This is to ensure that your work on each is distinct and that any work on a new project doesn’t get recorded as late work on an old project. However, you will continue with the same WolfScheduler project for GP3. You need to share the project to your GP3 repository.
Learning Outcomes
Disconnect a project from a GitHub repository
Share a project with a new GitHub repository
Push changes to a new remote GitHub repository
Best Practice: Version Control
A project can belong to only a single local repository. That is because a Git repository is represented as a folder on a file system with a special .git/ folder that stores Git metadata. A project cannot be in two locations on the file system at once. The process outlined below will let you share an existing Eclipse project with a new repository and then retain a copy of your Guided Project 2 submission in a local copy of your Guided Project 2 repository.
Complete the following steps to push WolfScheduler to your GP3 repository.
Clone your GP3 repository provided by the teaching staff. The pattern will be csc216-XXX-GP3-YYY, where XXX is your section number and YYY is your repository number.
Stage your WolfScheduler to your GP3 repository by right clicking on the project and selecting Team > Share Project.
In the Configure Git Repository pop-up, uncheck the option Use or create repository in parent folder of project. Then select your new repository from the drop down and check your project.
Now that your project is staged for the GP3 repository, add the files to the index by right clicking on the WolfScheduler project and selecting Team > Add to index. Then Commit and Push the project to start GP3! Use a commit message like “[Skeleton] Starting GP3 with GP2 WolfScheduler”.
Reset your GP2 WolfScheduler
When you shared WolfScheduler with your new GP3 repo, that removed the WolfScheduler directory from your local GP2 repo. If you want to keep a copy of your GP2 WolfScheduler, you should reset your local GP2 repository to the last commit in the remote. Then your local GP2 repository will have a copy of your final WolfScheduler submission for GP2. You can use that as a backup as you’re working on GP3!
Choose to do the reset in either Eclipse or Git Bash!
In Eclipse, do the following:
Right click on your GP2 repository in the Git Repositories view and select Reset.
Make sure the last commit is selected and select the option for a Reset type of Hard (HEAD, index, and working tree updated). Click Reset.
You will receive a warning about overwriting your local changes. This is what we want since we want to bring a copy of the project back into our local GP2 repository and erase all of your uncommitted local changes (which in this case is all the deleted WolfScheduler files). Click Reset.
Check that WolfScheduler is now in the Working Tree/ directory of your GP2 repository.
In Git Bash, do the following:
Change directory to your repository location.
Enter the command git reset --hard HEAD. This will reset the local repository to the same information as the remote and erase all of your uncommitted local changes (which in this case is all the deleted WolfScheduler files).
Caution: Resetting a Repo
In this case, resetting the repo is useful, but the reset command can lead to major problems. Use the power of the reset command carefully!
Set up your project to automatically run SpotBugs, PMD, and CheckStyle.
Check Your Submission
You should ALWAYS verify that your pushed changes are on the remote repository. You can do this by going to your repository on the GitHub website and making sure that your WolfScheduler project has been pushed to your GP3 repository.
Select the appropriate organization and repository.
Verify that your latest commit message is listed and that all pushed files are in the repository.
Verify that your Guided Project 3 Jenkins job is pulling your WolfScheduler from your GP2 repository (this job will start with GP3-. If Jenkins does not recognize your project, please notify the teaching staff via Piazza or the sup list as early as possible to ensure that your project is set up correctly for auto grading.
Caution: Red Ball on Jenkins is OK (at this point)
At this point, your Jenkins build will be a red ball. That is expected because there are several things that we have to do in Guided Project 3 before our code will even compile. The important thing is that the job runs and gives you a red ball!
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Skeleton]” for future you!
Independent Task: Write and Update System Test Plan
Test Driven Development (TDD) means that tests are written before code is written. For system level tests, that means they are written as soon as the requirements are finalized. You will use the WolfScheduler Requirements to write additional test cases for the conflict functionality. Additionally, you will update any test cases that are no longer appropriate once the conflict functionality is added.
Learning Outcomes
Write and update system tests
Create GP3 System Test Plan Document
If you don’t already have a project_docs folder in your WolfScheduler project, create it by right clicking on WolfScheduler and selecting New > Folder.
Download the document as a Word document by selecting File > Download > Microsoft Word in Google Drive.
Save your system test plan as a Word document (*.doc or *.docx)
Put the system test plan in the WolfSchedulerproject_docs folder.
Add the system test plan to the index of the repo and commit/push.
Add and Modify Tests
Read the WolfScheduler requirements. Guided Project 3 will focus the [Schedule Conflict] subflows of Use Cases 2 & 4 around time. The remaining requirements were implemented in Guided Projects 1 and 2.
With the new time conflict functionality, there are tests in the system test plan that were fine for Guided Project 2, but that no longer meet the requirements. These tests must be revised to meet the full WolfScheduler requirements.
Do the following tasks to update your system test plan:
Identify any tests that will fail with the conflict requirement fully implemented. Revise or rewrite the test(s). You MAY NOT remove them!
Write 5 additional tests that consider different conflict scenarios.
Additional instructions about the expectations for updating the system test plan for the WolfScheduler project are IN the System Test Plan document. Please follow those instructions (especially highlighting new or modifed tests) to help with grading and earning full credit for your work.
Complete the following tasks before pushing your work to GitHub.
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Test]” for future you!
Guided Task: Create Conflict Interface
Checking for a schedule conflict is a behavior that is useful for WolfScheduler and other applications. We will practice additive change by incorporating the checked conflict behavior as an interface. For the moment, the Conflict interface will be specific to the Activity hierarchy (i.e., we will be using classes in the Activity hierarchy rather than a generic type). You can update the Conflict interface to handle generic objects later.
Learning Outcomes
Create an interface
Create a custom exception class
Create an Interface
Create a new Java interface called Conflict in the edu.ncsu.csc216.wolf_scheduler.course package of the WolfScheduler project. To create an interface, do the following:
Right click on the edu.ncsu.csc216.wolf_scheduler.course package and select New > Interface.
Enter the name Conflict and check the option to generate comments.
Click Finish to create the interface.
Add a Method
Add the method checkConflict() to the Conflict interface. The method has a void return type and accepts an Activity object as a parameter. The method must throw a ConflictException, which is a custom checked exception that you will define next.
Since checkConflict() is abstract, its header must end in a semicolon rather than an opening curly brace. However, since the method is part of an interface, it is automatically public and abstract, so you can leave out those key words. The method header is:
There are several design considerations to think about when deciding how best to set up the method for the Conflict interface. The first involves identifying the best location to handle the check for conflict. There are two choices: 1) handle in the Activity hierarchy by comparing the current instance to a parameter instance, and 2) handle in the list of Activitys. By checking for conflict in the Activity hierarchy rather than in the list, you are following a design similar to the equals() and hashCode() methods from Object and similar to the Comparable interface. An alternative would be to handle in WolfScheduler; however that implementation would likely be clunky since there are two locations where you will need to check for conflict (addCourse() and addEvent()). Since Activity will implement the Conflict interface, the idea of conflict is built into the Activity hierarchy and any other program that might use that hierarchy.
A second design consideration is how to report a conflict to the client. There are two choices: 1) return a boolean specifying if there is conflict or not, and 2) throwing an exception. The first choice would work, but the client could easily ignore the return type and end up with a conflict! The second choice of throwing an exception is a stronger alternative. However, it depends on the type of exception that you throw. If you throw something like an IllegalArgumentException, that can also be ignored or not handled. IllegalArgumentException is an unchecked exception, and the compiler doesn’t provide feedback about handling it. It’s the developer’s responsibility to fully read the documentation to know to check for it to avoid a possible program crash (you did document all the methods that throw IllegalArgumentExceptions, right?). A checked exception requires the the developer do something to handle the exception or the code won’t even compile. You’ll be using a custom checked exception specifically for conflicts: ConflictException. This means that clients of the Conflict interface MUST handle the exceptions in their code!
The design for the full WolfScheduler project, including the Conflict interface and ConflictException is below.
Use the Quick Fix tool to create a ConflictException in the edu.ncsu.csc216.wolf_scheduler.course package. Since ConflictException is a checked exception, it should inherit from Exception. You’ll finish implementing ConflictException on the next page.
Comment Conflict
Interfaces define behaviors that implementing classes must define. That means the interface must be well commented so that others can use the interface for other classes. Fully Javadoc the interface at both the interface and method levels. Remember to use the @throws tag to describe when the ConflictException is thrown.
Complete the following tasks before pushing your work to GitHub.
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Implementation]” for future you!
Guided Task: Implement and Test ConflictException
Implementing and test a custom Exception.
Learning Outcomes
Implement and test a custom exception class
serialVersionUID Warning
Since the ConflictException class extends Exception, which implements the Serializable interface, the Java compiler will provide a warning about ConflictException lacking a serialVersionUID. An object and its state is serialized when the information about the object, including the current values in the fields are written to a file in a Java-specific format. Serialized files can then be deserialized or read in as Java objects. Serialization is a way of saving object state when an application is closed. Instead of using serialization, however, you are using specifically formatted files for saving state of our application.
One of the key items in serialization is that each object type requires a unique id so the serializier knows what object to work with when reading and writing Java objects. If you’re not planning to use serialization and you want to get rid of the compiler warning, you can use the default serialVersionUID of 1L (which is the value 1 stored into a long primitive type; a long takes twice the memory of an int). Use the Eclipse Quick Fix tool to create the default serialVersionUID. Make sure you comment the field. We suggest using the comment:
/** ID used for serialization. */privatestaticfinallongserialVersionUID=1L;
Exception Constructors
Any custom Exception requires the author to create two constructors. The first is a parameterized constructor with a String specifying a message for the Exception object. The message is passed to the parent’s constructor. The second is a parameterless constructor that calls the parameterized constructor with an author specified default message. A custom Exception needs no additional state or behavior.
Write the two constructors for ConflictException. The default message should be "Schedule conflict.".
Creating a Test Class
In Guided Project 1, you created a test class directly in the test/ folder. Eclipse provides additional support for generating JUnit tests. Follow the steps below to create ConflictExceptionTest.
Right click on ConflictException in the Package Explorer. Select New > JUnit Test Case.
Update the New JUnit Test Case wizard dialog with the following information:
New JUnit 4 test should be selected
Source folder: WolfScheduler/test
Check Generate comments
Click Next >. (If you click Finish that’s ok. You’ll have to either delete the class and start over or manually create the test methods.)
The next page lets you select the test methods that you want to create from the methods in the class under test. For ConflictExceptionTest, you want to test both constructors. Check ConflictException(String) and ConflictException(). Then click Finish.
Writing Test Methods
When writing unit tests, you first need to think about the goals of the test. Most of ConflictException’s functionality is inherited from Exception. Since that parent class has been extensively tested by the Java API authors, you should focus your attention on what is different and custom about the child class - the new constructors! The tests will use the new constructors and verify that the correct message is available.
Below is the test case for the parameterized constructor:
Write the test for the parameterless ConflictException constructor.
Comment ConflictException and ConflictExceptionTest
Javadoc both your ConflictException and the associated ConflictExceptionTest.
Run Your Tests
Run your new ConflictExceptionTest class and ensure the tests pass. You may need to update your ConflictException implementation or the tests until they pass.
Also, continue to run the other unit tests in your suite! Make sure they still pass, ensuring no regression in functionality.
Complete the following tasks before pushing your work to GitHub.
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Implementation]” and “[Test]” for future you!
Guided Task: Test Driven Development
In this section, you will start creating tests for the implementation of the Conflict interface in Activity.
Learning Outcomes
Create a test class
Implement a test for an exception
Best Practice: Test Driven Development (TDD)
Test Driven Development (TDD) is the process of using the requirements and object design to write the unit tests for a class first, and then use the tests to drive the implementation of the methods in the class. The process is typically iterative. Start with the simplest test first and implement the functionality to pass. Then add additional tests that consider other paths, edge cases, and error cases. After adding each test, implement the functionality to pass it (if needed).
TDD is a highly effective process for developing code that is well tested. By testing right away and letting the tests drive development, you will implement more high quality code more quickly and not run out of time for testing!
Implement the Conflict Interface in Activity
Since Activity contains the fields related to the meetingDays, startTime, and endTime, you can implement the Conflict.checkConflict() functionality in the Activity class. If the child classes need custom conflict implementation later, they can override the method.
You can use your tests to drive the creation of new methods in your code as in using Quick Fixes for Error-Driven Development. However, this may not be appropriate in all cases - Quick Fix will help you create the checkConflict() method, but will not know that the method is part of implementing the Conflict interface. Instead, you’ll set up the appropriate structure and generate a test class from the structure.
Follow these steps to set up Activity for testing the Conflict functionality:
Open Activity in the editor.
Update the Activity class header to include implements Conflict. When complete, the header should look like public abstract class Activity implements Conflict {.
Because Activity is an abstract class, there are no compiler errors about Activity needing an implementation of Conflict’s method. Instead, Course and Event won’t compile. However, you want Activity to contain the checkConflict() implementation. You can use Eclipse’s source generation tool to implement the method in Activity by right clicking in the editor and selecting Source > Override/Implement Methods. Check the option for Conflict.checkConflict(Activity) and click OK.
Now that Activity implements the Conflict.checkConflict() method, the compiler errors in Course and Event are resolved.
Reminder: Creating a JUnit Test Class from a Class Under Test
Create ActivityTest for Testing Conflict Functionality.
Now, create the JUnit class to test the Activity.checkConflict() implementation. Create a new JUnit test for Activity named ActivityTest in the edu.ncsu.csc216.wolf_scheduler.course package of the test/ folder in WolfScheduler. Since you’ve been provided test classes that test the other methods, you should only generate a test method for the checkConflict() method. After generating the test, you can remove the fail() statement since you are about to implement the test.
checkConflict() Overview
The checkConflict() method is an instance method of Activity. That means it compares the current instance (i.e., this) with the parameter possibleConflictingActivity to see if there is any conflict in their days and times. If there is a conflict, the checked ConflictException is thrown. If there is no conflict, nothing happens and the statement in the client code following the call to checkConflict() is executed.
Two Activity objects are in conflict if there is at least one day with an overlapping time. A time is overlapping if the minute is the same. For example, an Activity on Monday from 1:30PM-2:45PM would conflict with an Activity on Monday from 2:45PM-3:30PM because of the overlap on the 2:45 minute. You’ll encode this test shortly.
Start by Javadocing your checkConflict() method to describe how the conflict check works in the context of the Activity hierarchy.
Writing Tests
The following sections will detail the tests that you should write to fully test the checkConflict() method.
No Conflict Test
Since you are testing a method that may throw a checked exception, the test code must be surrounded with a try/catch block. For a no conflict test, the expected result is that the exception is NOT thrown, so the initial test structure is:
try{//Call to checkConflict}catch(ConflictExceptione){fail();//If an exception is thrown when there is no conflict the test fails.}
The rest of the test is creating the two Activity objects to check for conflict, and then to check the conflict. Since Activity is abstract, you cannot directly construct Activity. Instead, you must construct a Course or Event object for the comparison.
Activitya1=newCourse("CSC 216","Software Development Fundamentals","001",3,"sesmith5","MW",1330,1445);Activitya2=newCourse("CSC 216","Software Development Fundamentals","001",3,"sesmith5","TH",1330,1445);
Next, the checkConflict() method is called on one of the objects. The other is passed as a parameter. Since the call could cause an exception, the call to checkConflict() is placed in the try block.
Finally, there should be some type of assert in the body of the try block. It’s not enough that no exception was thrown. Instead, you need to check that the call to the checkConflict() method did NOT lead to a change of any of the state of the Activity object related to the meetingDays, startTime, and endTime for either object. You can check all three at once by calling the getMeetingString() method.
The final test looks like the following:
@TestpublicvoidtestCheckConflict(){Activitya1=newCourse("CSC 216","Software Development Fundamentals","001",3,"sesmith5","MW",1330,1445);Activitya2=newCourse("CSC 216","Software Development Fundamentals","001",3,"sesmith5","TH",1330,1445);try{a1.checkConflict(a2);assertEquals("Incorrect meeting string for this Activity.","MW 1:30PM-2:45PM",a1.getMeetingString());assertEquals("Incorrect meeting string for possibleConflictingActivity.","TH 1:30PM-2:45PM",a2.getMeetingString());}catch(ConflictExceptione){fail("A ConflictException was thrown when two Activities at the same time on completely distinct days were compared.");}}
Run the test. Since you have not yet implemented checkConflict(), the test should pass!!!? For now, that’s ok. We will shortly be implementing the checkConflict() method once we have a failing test to pass. However, our happy path test is an important test to ensure that as you implement the rest of checkConflict() that you do not start finding a conflict where there should be no conflict!
Testing an Exception - Conflict Test
The next test type to consider is a test when there is a conflict. In this case, the expected results are that the ConflictException is thrown. Continuing execution without an exception is a failing test. The initial test structure is:
try{//Call to checkConflictfail();//If the test reaches this point it fails since the exception was NOT thrown}catch(ConflictExceptione){//Check that no state was changed}
We can continue the test in the same test method as above, or we can create a new test method to consider this specific case. By continuing with the previous test method, we can work with the existing local variables a1 and a2 and modify the days or times to lead to a conflict. By writing another test method, we can more clearly identify which functionality is passing or failing. The example below is a separate test method, but you are welcome to continue the implementation in the other test method.
@TestpublicvoidtestCheckConflictWithConflict(){Activitya1=newCourse("CSC 216","Software Development Fundamentals","001",3,"sesmith5","MW",1330,1445);Activitya2=newCourse("CSC 216","Software Development Fundamentals","001",3,"sesmith5","M",1330,1445);try{a1.checkConflict(a2);fail("A ConflictException was NOT thrown when two Activities had a day/time conflict.");}catch(ConflictExceptione){assertEquals("Incorrect meeting string for this Activity.","MW 1:30PM-2:45PM",a1.getMeetingString());assertEquals("Incorrect meeting string for possibleConflictingActivity.","M 1:30PM-2:45PM",a2.getMeetingString());}}
Run the test. It fails! But that is exactly what you want. It means you have a test that you can use to start driving the implementation of the checkConflict() functionality in Activity, which you’ll work on in the next step.
Testing Behaviors to Avoid
There are behaviors that you want to avoid when testing. PMD will flag some of these behaviors with notifications (and a PMD notification leads to a loss of a point AND PMD testing notifications will block the teaching staff tests from running on your code!)
Test Method with No assert*()
Every test method should have at least one assert*() method call. The goal of a test method is to assert or check something about the code under test to verify the code is working correctly. It’s very tempting to leave out an assert*() method call because your test method will pass (and you may also achieve coverage). But that means you can have no confidence in your code! Always write a test method to assert something about the code under test!
Meaningless assert*() Statements
A meaningless assert*() statement is something like assertTrue(true);, assertFalse(false);, or assertNull(null);. There is ALWAYS something to check about the code under test. At a minimum you can check that the state has not changed or that the type is correct. Avoid meaningless assert statements. If you need help identifying what you should check, contact the teaching staff.
Complete the following tasks before pushing your work to GitHub.
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Test]” for future you!
Check Jenkins results for a yellow ball! At this point, your code should compile against the teaching staff tests.
Independent Task: Implement Conflict in Activity
Use TDD to implement the checkConflict() method in Activity.
Learning Outcomes
Use test driven development to implement and test a method.
Best Practice: Test Driven Development (TDD)
Test Driven Development (TDD) is the process of using the requirements and object design to write the unit tests for a class first, and then use the tests to drive the implementation of the methods in the class. The process is typically iterative. Start with the simplest test first and implement the functionality to pass. Then add additional tests that consider other paths, edge cases, and error cases. After adding each test, implement the functionality to pass it (if needed).
TDD is a highly effective process for developing code that is well tested. By testing right away, and letting the tests drive development, you will implement more high quality code more quickly and not run out of time for testing!
Reminder: Resources on Writing Tests
There are several resources provided for writing tests, including sample test code:
Use the provided tests from Guided Projects 1 & 2 to help you write your own tests for Guided Project 3!
Test and Implement Activity.checkConflict()
Use TDD to implement the checkConflict() method in Activity. The teaching staff has provided a list of tests to consider, but this is by no means an exhaustive list or all of the tests that the teaching staff will evaluate you on. Use your testing skills to develop an extensive set of tests for Activity.checkConflict().
Tests for consideration:
No conflict test where a2.checkConflict(a1) is called. This switches the this and parameter to make sure that the method is commutative.
Conflict on a single day.
Conflict where the endTime for this is the same as the startTime for possiblyConflictingActivity.
As you write each test (which can continue in the same method or a separate test method for each test), update Activity.checkConflict() as appropriate to test the new tests without failing the old tests (or any other tests in the test suite).
When you are confident that your Activity.checkConflict() method is working correctly, you can move on to the next steps.
Jenkins and Teaching Staff Tests
Now that you are writing your own tests, the teaching staff unit tests will be hidden from you. The teaching staff tests will run if 1) you have no testing-related PMD notifications and 2) you have greater than 80% statement coverage on non-UI classes. Teaching staff test are listed with a starting TS_. A failing teaching staff test has a hint that is provided so that you can try creating your own version of the test locally for debugging. If you have a question about a hint, please post it to Piazza for clarification.
When working on your projects, you may end with lots of teaching staff test failures. That’s ok! They are there to help you improve your code. However, you need to be strategic in how to go about fixing them. You can’t just start at the top of the list and work your way down. Because we utilized composition relationships, certain classes have dependencies on other classes. You should focus on fixing the contained classes before fixing the containers. For WolfScheduler, you should focus on fixing bugs in the Activity hierarchy before fixing bugs in WolfScheduler!
Document Activity.checkConflict()
As always, you should document the method with details about the implementation that would help a client of Activity work with the method correctly in their own code!
Complete the following tasks before pushing your work to GitHub.
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Implementation]” and “[Test]” for future you!
Check Jenkins results for a yellow ball. Fix any failing tests in TS_ActivityTest and in the course package. Test failures in the scheduler package can wait until later in the Guided Project.
Independent Task: Coverage
Use EclEmma to evaluate coverage on checkConflict() method in Activity.
Learning Outcomes
Use coverage to determine if additional tests are needed to execute other, uncovered, paths
Best Practice: Coverage
Coverage is a measure of the code executed when running a test case. The principle behind coverage is that a line of code that has been executed is less likely to contain a bug than a non-executed line of code. There are four levels of coverage:
Method coverage: every method called at least once.
Statement/Line coverage: every statement executed at least once.
Branch coverage: every conditional statement evaluated on both its true and false paths.
Condition coverage: every predicate in a compound conditional evalue on both its true and false paths.
For CSC 216, the teaching staff expects that you will achieve at least 80% statement coverage on each model class (which are any of the classes not in the ui package). A coverage tool can identify paths that you have not yet executed with your tests. Using the feedback you can write additional tests to exercise other portions of your code.
There are some limitations to code coverage. Since you are using your existing code to drive your testing, your tests won’t tell you if you’re missing functionality. That is one reason why you should use TDD to drive the implementation and a reason for why we have multiple types of testing - the missing functionality might be caught in system level tests.
Also, don’t write poor tests just to make your coverage tool happy. Take the time to write a meaningful test so that you may have increased confidence that your code works correctly. PMD will provide notifications on poor test practices!
Run Tests for Coverage
To run tests for coverage, right click on the test/ source folder and select Coverage As > JUnit Test.
When evaluating coverage locally, focus only on the coverage of classes that are not in the *.ui package. The coverage of the test classes themselves and any user interface classes are not considered when evaluating the coverage of your tests. Jenkins is set up to exclude test classes and user interface classes, but your local EclEmma is not. Don’t panic if the numbers seem low.
One last thing to check is that EclEmma is reporting the right metrics. Click the down arrow (View Menu) option in the Coverage view. Select Line Counters for seeing statement/line coverage. If you want to see condition coverage, use the Branch Counters option.
Reminder: Resources on Coverage
There are several resources provided about code coverage:
The CSC 216 Teaching Staff expect 80% statement coverage on every non-UI and non-test class. While the unit tests provided by the teaching staff for the rest of the project are extensive, they may not achieve 80% coverage on your implementation. Add additional tests to reach the 80% coverage threshold for all non-UI and non-test classes: Activity, Course, Event, ConflictException, ActivityRecordIO, CourseRecordIO, and WolfScheduler.
Jenkins and Teaching Staff Tests
Now that you are writing your own tests, the teaching staff unit tests will be hidden from you. The teaching staff tests will run if 1) you have no testing-related PMD notifications and 2) you have greater than 80% statement coverage on non-UI classes. Teaching staff test are listed with a starting TS_. A failing teaching staff test has a hint that is provided so that you can try creating your own version of the test locally for debugging. If you have a question about a hint, please post it to Piazza for clarification.
When working on your projects, you may end with lots of teaching staff test failures. That’s ok! They are there to help you improve your code. However, you need to be strategic in how to go about fixing them. You can’t just start at the top of the list and work your way down. Because we utilized composition relationships, certain classes have dependencies on other classes. You should focus on fixing the contained classes before fixing the containers. For WolfScheduler, you should focus on fixing bugs in the Activity hierarchy before fixing bugs in WolfScheduler!
When you look at Jenkins feedback at this step, make sure you look at the console output. That provides a listing of the statement coverage for your non-UI classes and will let you know about PMD notifications. If the teaching staff test cases are blocked, the statement SKIPPING TEACHING STAFF TEST CASES will be displayed. There are some sample console outputs below. Note, you can ignore the output lines about the Nashorn engine.
PMD Notification There is a statement Project contains X JUnit-related PMD alerts, which leads to the statement SKIPPING TEACHING STAFF TEST CASES.
11:26:14 [echo] ---> Checking coverage thresholds for source files
11:26:14 [script] Warning: Nashorn engine is planned to be removed from a future JDK release
11:26:14 [exec] Project contains 1 JUnit-related PMD alerts
11:26:14 [exec] Result: 1
11:26:14 [script] Warning: Nashorn engine is planned to be removed from a future JDK release
11:26:14 [echo]
11:26:14 [echo] *****SKIPPING TEACHING STAFF TEST CASES*****
Coverage Report - Insufficient Coverage If the statement/line coverage (last column) for one or more classes is under 80%, a message of TOTAL of X classes not meeting the 80% line coverage threshold will be displayed, which leads to the statement of SKIPPING TEACHING STAFF TEST CASES.
11:55:08 [exec] CLASS METHOD COVERAGE LINE COVERAGE
11:55:08 [exec] Activity 100 83
11:55:08 [exec] Event 90 93
11:55:08 [exec] Course 82 72
11:55:08 [exec] ConflictException 100 100
11:55:08 [exec] CourseRecordIO 66 97
11:55:08 [exec] ActivityRecordIO 50 85
11:55:08 [exec] WolfScheduler 0 0
11:55:08 [exec] TOTAL of 2 classes not meeting the 80% line coverage threshold
11:55:08 [exec] Result: 1
11:55:08 [script] Warning: Nashorn engine is planned to be removed from a future JDK release
11:55:08 [echo]
11:55:08 [echo] *****SKIPPING TEACHING STAFF TEST CASES*****
Coverage Report - No Problems The statement/line coverage (last column) exceeds the 80% threshold per class. There is a statement that a TOTAL of 0 classes not meeting the 80% line coverage threshold and there are lines stating that the teaching staff tests are building and executing. You can see the full results, including hints, in the test reports.
11:40:24 [exec] CLASS METHOD COVERAGE LINE COVERAGE
11:40:24 [exec] Activity 100 91
11:40:24 [exec] Event 100 100
11:40:24 [exec] Course 100 93
11:40:24 [exec] ConflictException 100 100
11:40:24 [exec] CourseRecordIO 66 97
11:40:24 [exec] ActivityRecordIO 50 85
11:40:24 [exec] WolfScheduler 100 93
11:40:24 [exec] TOTAL of 0 classes not meeting the 80% line coverage threshold
11:40:24 [script] Warning: Nashorn engine is planned to be removed from a future JDK release
11:40:24 [script] Warning: Nashorn engine is planned to be removed from a future JDK release
11:40:24 [echo]
11:40:24 [echo] ---> Building Teaching Staff Tests
11:40:25 [echo]
11:40:25 [echo] ---> Executing Teaching Staff unit tests
Complete the following tasks before pushing your work to GitHub.
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Test]” for future you!
Check Jenkins results for a yellow ball! Fix any coverage issues in the course and io packages. Other test failures and coverage issues from the scheduler package can wait for the next step.
Independent Task: Update WolfScheduler
There are several updates required for WolfScheduler to work with the Activity.checkConflict functionality. WolfScheduler will catch any ConflictExceptions and throw IllegalArgumentExceptions out to the client.
Learning Outcomes
Refactor WolfScheduler to check for conflict
Update WolfSchedulerTest and WolfScheduler
WolfScheduler should check for possible conflicts when addCourseToSchedule() and addEventToSchedule() are called. Update WolfSchedulerTest to include tests for the conflict functionality.
Then update addCourseToSchedule() and addEventToSchedule() to pass the tests. WolfScheduler will catch any ConflictExceptions and throw IllegalArgumentExceptions out to the client with appropriate messages for the dialog boxes. If a ConflictException is caught in addCourseToSchedule() and IllegalArgumentException is thrown with the message "The course cannot be added due to a conflict.". If a ConflictException is caught in addEventToSchedule() and IllegalArgumentException is thrown with the message "The event cannot be added due to a conflict.".
You may need to go back and update some of the tests that were provided with WolfSchedulerTest! The tests were written before the time conflict functionality was added and will need to be modified based on the full implementation. You cannot delete the provided test methods, but you can revise them to check that the appropriate exceptions are thrown and that the correct schedule information is there afterwards (i.e., without the conflicting activity).
Jenkins and Teaching Staff Tests
Now that you are writing your own tests, the teaching staff unit tests will be hidden from you. The teaching staff tests will run if 1) you have no testing-related PMD notifications and 2) you have greater than 80% statement coverage on non-UI classes. Teaching staff test are listed with a starting TS_. A failing teaching staff test has a hint that is provided so that you can try creating your own version of the test locally for debugging. If you have a question about a hint, please post it to Piazza for clarification.
When working on your projects, you may end with lots of teaching staff test failures. That’s ok! They are there to help you improve your code. However, you need to be strategic in how to go about fixing them. You can’t just start at the top of the list and work your way down. Because we utilized composition relationships, certain classes have dependencies on other classes. You should focus on fixing the contained classes before fixing the containers. For WolfScheduler, you should focus on fixing bugs in the Activity hierarchy before fixing bugs in WolfScheduler!
Run Coverage
Ensure that WolfScheduler has 80% or higher statement coverage. If not, use the coverage feedback to create more high quality tests.
Complete the following tasks before pushing your work to GitHub.
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Implementation]” and “[Test]” for future you!
Check Jenkins results for a green ball! Fix any Jenkins issues.
Independent Task: Update WolfSchedulerGUI
Black box test the WolfScheduler system to ensure that the conflict functionality is working correctly from end-to-end.
Learning Outcomes
Run (and revise) black box tests
WolfSchedulerGUI
WolfSchedulerGUI did not require any changes from GP2. But if you need a new copy, copy the code from WolfSchedulerGUI.java into your Eclipse file. Overwrite the entirety of the old file.
Run System Tests
Run your system tests! If any are still failing, use the debugger to help you find the problem. Report the actual results of your tests.
If you need to revise your tests to more correctly describe the inputs or outputs, you may do so.
Record the actual results of execution for your tests. If you run low on time, remember it is better to record the actual test failure; you’ll get credit for reporting what’s actually happening.
Make sure that you successfully push your System Test Plan to the remote GitHub repository! Check the website to make sure it’s there!
If you haven’t been commenting your code all along, go back and comment your code with Javadoc. All classes, methods, and fields should be commented. If you have been commenting as you have implemented the Activity hierarchy, go back and double check that the comments are up to date for the implemented functionality.
Make sure you include comments for overridden method that describe why the override was important.
Generate Javadoc for WolfScheduler. You may want to delete the existing doc/ folder before generating a new one to ensure that all Javadoc is updated. Double check that everything was generated!
Make sure that all code is pushed to GitHub by the assignment deadline (check all the files are on the remote!). There are deductions for any late work up to 48 hours.
Grading Rubric
Your Wolf Scheduler Guided Project 3 will be evaluated on the following items:
Overall Rubric
Phase
Grade Item
Points
Details
Total Points
0
Deductions
Grade Item
Points
Details
Misnamed file or incorrect project structure
-5
Incorrect names of files or incorrect project structure. This can include problems when importing the project to Eclipse for acceptance testing, incorrect location of the system test file, incorrect file extension, etc.
Other deductions
-5
If the project has to be manually graded due, you will receive a 5 point deduction. Make sure that your project builds on Jenkins!
Complete the following tasks before pushing your work to GitHub.
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Final Submission]” for future you!
Check Jenkins results for a green ball! Fix any Jenkins issues.
Grading Rubric
Your Wolf Scheduler Guided Project 3 will be evaluated on the following items:
Overall Rubric
Phase
Grade Item
Points
Details
Total Points
0
Deductions
Grade Item
Points
Details
Misnamed file or incorrect project structure
-5
Incorrect names of files or incorrect project structure. This can include problems when importing the project to Eclipse for acceptance testing, incorrect location of the system test file, incorrect file extension, etc.
Other deductions
-5
If the project has to be manually graded due, you will receive a 5 point deduction. Make sure that your project builds on Jenkins!
Javadoc Rubric
Item
Strong - 3 points
Adequate - 2 points
Inadequate - 1 point
Jenkins Server
We are using the Jenkins Continuous Integration system to automatically evaluate your work and provide feedback on your submission. Go to the [Guided Project and Project Jenkins Server by using https://csc216-jenkins.csc.ncsu.edu.