Bookmarks - A major refactoring

In a previous post I wrote about changing the interface that the Bookmarks class would implement from IDictionary to ICollection<Bookmark>. In order to do this I want to look at the test list and see if there are any new tests or tests that we had planned on that are no longer needed. Here is the updated test list:

  • Count == 0  
  • Add a Bookmark (label, URL), Count == 1 - This one is still needed but since I changed the interface it should read Add(Bookmark), Count == 1 
  • Add a Bookmark, remove a Bookmark, Count == 0
  • Remove a Bookmark, verify that it has been removed
  • Add 2 Bookmarks, remove a Bookmark, Count == 1
  • Add a Bookmark, call Contains, should return true 
  • Add 2 Bookmarks, Retrieve each by label
  • Add a Bookmark with a null label, expect ArgumentNullException
  • Add a Bookmark with a null URL, expect ArgumentNullException
  • Add a Bookmark, add another with the same label, expect ArgumentException
  • Retrieve a Bookmark that is not in the collection, return null 
  • Add a Bookmark, replace the URL with an Item property, verify that the Bookmark has been updated
  • Contains() returns false
  • Add 2 Bookmarks, call Clear(), Count == 0
  • Add 3 Bookmarks, call GetEnumerator and verify that the 3 Bookmarks are enumerated
  • Add 2 Bookmarks, call CopyTo(Bookmark[], int) and verify the bookmarks are copied

The first step is to walk through the interface changes. Let's do that one test at a time starting at the first test. The easiest way to do this is to comment out all the tests or use [Ignore] it's up to you. Once they are commented out uncomment each one individually, modify the test, see if the test passes, if not modify the code to make the test pass. After doing this the Bookmarks class looks like this:

using

System;
using System.Collections;
using System.Collections.Generic;

public

class Bookmarks : ICollection<Bookmark>
{
   private Dictionary<string, Bookmark> dictionary =
      new Dictionary<string, Bookmark>();

   public int Count
{
      get { return dictionary.Count; }
}

   public void Add(Bookmark bookmark)
{
      if (bookmark.Label == null || bookmark.Uri == null)
         throw new ArgumentNullException();

dictionary[bookmark.Label] = bookmark;
}

   public bool Remove(Bookmark bookmark)
{
      return dictionary.Remove(bookmark.Label);
}

   public Uri this[Bookmark bookmark]
{
      get 
{
         if (!dictionary.ContainsKey(bookmark.Label)) return null;
         return dictionary[bookmark.Label].Uri;
}
      set
      {
Add(bookmark);
}
}

   public IEnumerator<Bookmark> GetEnumerator()
{
      foreach
(KeyValuePair<string, Bookmark> association in dictionary)
            yield return association.Value;
}

   public bool Contains(Bookmark item)
{
throw new
Exception("The method or operation is not implemented.");
}

   // the remaining methods are implemented the same way as
// Contains until tests are written
}

That was fairly straight forward. A note to self; next time I implement a class that inherits from an interface I need to make sure the class implements the interface right from the start and just have each unimplemented method throw an exception. That way the tests conform to the interface right from the start. To summarize all the tests have been updated and the Bookmarks class now implements ICollection<Bookmark>; the major refactoring is now complete. The following is the test list with the appropriate tests checked off:

  • Count == 0  
  • Add a Bookmark (label, URL), Count == 1 - This one is still needed but since I changed the interface it should read Add(Bookmark), Count == 1 
  • Add a Bookmark, remove a Bookmark, Count == 0
  • Remove a Bookmark, verify that it has been removed
  • Add 2 Bookmarks, remove a Bookmark, Count == 1
  • Add a Bookmark, call Contains, should return true 
  • Add 2 Bookmarks, Retrieve each by label
  • Add a Bookmark with a null label, expect ArgumentNullException
  • Add a Bookmark with a null URL, expect ArgumentNullException
  • Add a Bookmark, add another with the same label, expect ArgumentException
  • Retrieve a Bookmark that is not in the collection, return null 
  • Add a Bookmark, replace the URL with an Item property, verify that the Bookmark has been updated
  • Contains() returns false
  • Add 2 Bookmarks, call Clear(), Count == 0
  • Add 3 Bookmarks, call GetEnumerator and verify that the 3 Bookmarks are enumerated
  • Add 2 Bookmarks, call CopyTo(Bookmark[], int) and verify the bookmarks are copied

The next step will be to implement the Contains method. Until next time...