/* * Krysta Yousoufian, CSE 331, 10/23/11 * * Test class for Schedule. Written as an example of good unit tests, both in * terms of WHAT to test (input conditions, edge cases, boundary cases) and HOW * to test (code organization, asserts, etc.) * * Note that the commented section delimiters are not necessary. But they will make * the grader love you if you have a lot of tests. :) * * See http://www.cs.washington.edu/education/courses/cse331/11au/sections/schedule/Schedule.html * for the specs being tested against. Clicking on the method names takes you to the * source code, which might be useful for seeing the format of the Javadoc comments. * Otherwise, the methods are all just boring stubs. */ package schedule; import java.util.*; import org.junit.*; import static org.junit.Assert.*; public class ScheduleTest { private final String defaultName = "Basic Schedule"; private final String defaultApptDesc = "Very important date "; private final int defaultMaxAppts = 3; // Fields for default date for an Appointment private final int defaultYear = 2001; private final int defaultMonth = 3; private final int defaultDay = 14; private Calendar defaultDate; private Schedule sched; // ----------------------- Helper and setup methods ----------------------- // // Set up default date @BeforeClass public void setUpBeforeClass() { defaultDate = Calendar.getInstance(); defaultDate.set(2001, 3, 14); } // Create a new Schedule for each test @Before public void setUp() throws Exception { sched = new Schedule(defaultName); } // Creates a new Appointment with the current date and description private Appointment createAppt(int i) { return createAppt(0, defaultDate); } // Creates a new Appointment for the given date with i appended to the // default description private Appointment createAppt(int i, int year, int month, int date) { Calendar cal = Calendar.getInstance(); cal.set(year, month, date); return createAppt(i, cal); } // Creates a new Appointment for the given date with i appended to the // default description private Appointment createAppt(int i, Calendar date) { return new Appointment(date, defaultApptDesc + i); } // ----------------------- Tests for [get|set]MaxApptsPerDay() ----------------------- // // Test that getMaxApptsPerDay returns -1 initially @Test public void test_getMaxApptsInitial() { assertEquals(-1, sched.getMaxApptsPerDay()); // Note that EXPECTED value goes // on left, ACTUAL value on right } // Test that max appts per day is -1 (unlimited) after being set to // an arbitrary negative value // (KY notes: the test_setMaxAppts methods are a good example of // needing to test a getter and a setter together) @Test public void testChangeMaxApptsToNegative() { sched.setMaxApptsPerDay(-10); assertEquals(-1, sched.getMaxApptsPerDay()); } // Test that max appts per day is -1 (unlimited) after being set to -1 @Test public void testChangeMaxApptsToNegative1() { // Since the initial limit is -1, it's probably a good idea to change // it to something else first. If the limit is not changed correctly the // first time, it's possible that this test will pass incorrectly; however, // a different test should fail, and once that one is fixed, this one // should run normally. sched.setMaxApptsPerDay(-1); assertEquals(-1, sched.getMaxApptsPerDay()); } // Test that max appts per day can be set to 0 @Test public void testChangeMaxApptsTo0() { sched.setMaxApptsPerDay(0); assertEquals(0, sched.getMaxApptsPerDay()); } // Test that max appts per day can be set to a positive value @Test public void testChangeMaxApptsToPos() { sched.setMaxApptsPerDay(10); assertEquals(10, sched.getMaxApptsPerDay()); } // Test that an exception is thrown when setting the limit of // appts per day to a value lower than the actual number of appointments // contained for some day // (KY notes: this is a case where you have no choice but to implicitly // test add(). That's OK, because there are other methods testing add() // without setMaxApptsPerDay() and vice versa.) @Test(expected = IllegalArgumentException.class) public void testChangeMaxApptsTooLow() { sched.add(createAppt(1)); sched.add(createAppt(2)); sched.setMaxApptsPerDay(1); } // ----------------------------- Tests for add() ----------------------------- // // Sets max appointments per day to defaultMaxAppts, then adds that many appointments // on the default day public void setAndAddMaxAppts() { sched.setMaxApptsPerDay(defaultMaxAppts); for (int i = 0; i < defaultMaxAppts; i++) { sched.add(createAppt(i)); } } // Tests that a single appointment can be added to the Schedule. @Test public void testAddOne() { Appointment appt = createAppt(1); assertTrue(sched.add(appt)); assertTrue(sched.contains(appt)); } // Tests that several appointments can be added to the Schedule. @Test public void testAddSeveral() { Appointment appt1 = createAppt(1); Appointment appt2 = createAppt(2); Appointment appt3 = createAppt(3, defaultYear, defaultMonth, defaultDay - 1); assertTrue(sched.add(appt1)); assertTrue(sched.add(appt2)); assertTrue(sched.add(appt3)); assertTrue(sched.contains(appt1)); assertTrue(sched.contains(appt2)); assertTrue(sched.contains(appt3)); } // Tests that an appointment cannot be added twice. @Test public void testAddTwice() { Appointment appt = createAppt(1); assertTrue(sched.add(appt)); assertFalse(sched.add(appt)); assertTrue(sched.contains(appt)); } // Test that up to the max number of appointments per day can // be added. @Test public void testAddUpToMaxAppt() { sched.setMaxApptsPerDay(defaultMaxAppts); for (int i = 0; i < defaultMaxAppts; i++) { String msg = "Should be able to add " + defaultMaxAppts + " appointments but could only add " + i; Appointment appt = createAppt(i); assertTrue(msg, sched.add(appt)); assertTrue(msg, sched.contains(appt)); } } // Tests that no more than the max number of appointments can be added. @Test public void testAddMoreThanMaxAppt() { setAndAddMaxAppts(); Appointment appt = createAppt(defaultMaxAppts); assertFalse("Schedule added an appointment when it shouldn't", sched.add(appt)); assertFalse("Schedule contains an appointment it shouldn't have added", sched.contains(appt)); } // Test that after reaching the max number of appointments, you can add more // by increasing the limit @Test public void testAddIncreaseMaxLimit() { setAndAddMaxAppts(); sched.setMaxApptsPerDay(defaultMaxAppts+1); Appointment appt = createAppt(defaultMaxAppts); assertTrue(sched.add(appt)); assertTrue(sched.contains(appt)); } // Test that after reaching the max number of appointments, you can add more // if you remove the limit entirely @Test public void testAddSetMaxLimitUnlimited() { setAndAddMaxAppts(); sched.setMaxApptsPerDay(-1); Appointment appt = createAppt(defaultMaxAppts); assertTrue(sched.add(appt)); assertTrue(sched.contains(appt)); } // Test that no appointments can be added with a max of 0 @Test public void testAddMaxLimit0() { sched.setMaxApptsPerDay(0); Appointment appt = createAppt(1); assertFalse(sched.add(appt)); assertFalse(sched.contains(appt)); } // Test that the max number of appointments is enforced only per day, and // not over the total number of appointments @Test public void testMaxLimitOverSeveralDays() { sched.setMaxApptsPerDay(2); Appointment appt1 = createAppt(1); Appointment appt2 = createAppt(2); Appointment appt3 = createAppt(3, defaultYear, defaultMonth, defaultDay - 1); assertTrue("Couldn't add first appointment", sched.add(appt1)); assertTrue("Couldn't add second appointment (limit for one day)", sched.add(appt2)); assertTrue("Couldn't add appointment after reaching limit on another day", sched.add(appt3)); } // ----------------------- Tests for getApptsForDate() ----------------------- // // Test that an exception is thrown when passing in a negative day @Test(expected = IllegalArgumentException.class) public void testGetApptsForDateNegativeDay() { sched.getApptsForDate(2000, 1, -1); } // Test that an exception is thrown when passing in a day of zero @Test(expected = IllegalArgumentException.class) public void testGetApptsForDateZeroDay() { sched.getApptsForDate(2000, 1, 0); } // Test that an exception is thrown when passing in a day > 31 @Test(expected = IllegalArgumentException.class) public void testGetApptsForDateLargeDay() { sched.getApptsForDate(2000, 1, 32); } // Repeat the three tests above for month and year, omitted here for brevity... // see, this is why you should REALLY pass in a Calendar object rather than // month, day, and year. (But that wouldn't provide such an interesting unit // test example). If you had to write code like this and could see that the // input validation checks in your code were very similar, a white-box tester // might realistically only test, say, a negative value for year, a zero value // for month, and an overly large value for day. But for now, you should test // everything. // Test that an exception is thrown when passing in an invalid date (Feb. 29th // in a non-leap year) @Test(expected = IllegalArgumentException.class) public void testGetApptsForDateInvalidDate() { sched.getApptsForDate(2003, 2, 29); } // Test that appointments are retrieved for a leap day in a leap year @Test public void testGetApptsForDateLeapYear() { sched.add(createAppt(1, 2004, 2, 29)); List appts = sched.getApptsForDate(2004, 2, 29); assertEquals("Added one appointment to leap day", 1, appts.size()); } // Test that no appointments are returned for an empty schedule @Test public void testGetApptsForDateEmptySchedule() { List appts = sched.getApptsForDate(2000, 1, 1); assertEquals(0, appts.size()); } // Basic test that an appointment is returned. @Test public void testGetApptsForDateOneAppt() { Appointment appt = createAppt(1); sched.add(appt); List appts = sched.getApptsForDate(defaultYear, defaultMonth, defaultDay); assertEquals(1, appts.size()); assertEquals(appt, appts.get(0)); } // Test that when the schedule contains multiple appointments for // one day, all the appointments for the requested date are returned @Test public void testGetApptsForDateManyApptsOneDay() { ArrayList addedAppts = new ArrayList(); int numAppts = 3; for (int i = 0; i < numAppts; i++) { Appointment appt = createAppt(i); addedAppts.add(appt); sched.add(appt); } List retAppts = sched.getApptsForDate(defaultYear, defaultMonth, defaultDay); assertEquals("Didn't return all appointments", addedAppts.size(), retAppts.size()); for (int i = 0; i < numAppts; i++) { assertTrue(retAppts.contains(addedAppts.get(i))); } } // Test that appointments for other dates are not returned @Test public void testGetApptsForDateOnlyRequestedDate() { Appointment expected = createAppt(1); Appointment early = createAppt(2, defaultYear, defaultMonth, defaultDay - 1); Appointment late = createAppt(3, defaultYear, defaultMonth, defaultDay + 1); sched.add(expected); sched.add(early); sched.add(late); List appts = sched.getApptsForDate(defaultYear, defaultMonth, defaultDay); assertEquals("Didn't return all and ONLY appointments for requested date", 1, appts.size()); assertTrue(appts.contains(expected)); } // ----------------------------- Other tests ----------------------------- // // Test that schedule has the name given in the constructor @Test public void testGetNameBasic() { assertEquals(defaultName, sched.getName()); } // Test that Schedule doesn't do anything special to an empty name // (KY notes: there's no reason to expect that Schedule would handle empty // strings differently, so you may easily decide that this test isn't necessary. // If doing pure white-box testing, you probably wouldn't test // getName at all because it's so simple. If doing pure black-box testing, // you can't be sure that getName doesn't do something funky with empty strings.) @Test public void testGetNameEmptyString() { Schedule empty = new Schedule(""); assertEquals(defaultName, empty.getName()); } // Test that contains() returns false for an appointment not in the Schedule. // Case of contains() returning true is tested implicity in tests for add(); // adding a separate test would be redundant. @Test public void testContainsNotInSchedule() { Appointment appt = createAppt(1); assertFalse(sched.contains(appt)); } }