Toby and the complete stab in the dark

During my rather long wait for the plane in Dublin this morning, I had the opportunity to revisit Toby and his company's EDM. Unfortunately, by the time I got to some stories, I was actually in the air with no internet access and as such had to just leave them for now. Regardless, here's an initial stab at the user stories I mentioned a while back (with perhaps a couple additions.) I'm now wondering if these wouldn't be better expressed with Behave# :)

(Aside: The code is going up on Codeplex as soon as the project is approved)

[TestFixture]
public class Class1
{
  //* The administrator can login to the system.
  [Test]
  public void CanLogIntoSystemAsAnAdministrator()
{
      EDMSystem system = new EDMSystem();

system.LoginAs(UserType.Administrator);

      Assert.Equal<UserType>(
             UserType.Administrator, system.CurrentUser);
}

  //* The administrator can create a project.
  [Test]
  public void TheAdministratorCanCreateAProject()
{
      EDMSystem system = new EDMSystem();
system.LoginAs(UserType.Administrator);

system.CreateProject(0, "my first project");

      Assert.Equal<int>(1, system.ProjectCount);
}

  //* Non-administrative users can login to the system.
  [Test]
  public void CanLogIntoTheSystemAsANonAdministrativeUser()
{
      EDMSystem system = new EDMSystem();

system.LoginAs(UserType.Bob);

      Assert.Equal<UserType>(UserType.Bob, system.CurrentUser);
}

  //* A user can find a project by number.
  [Test]
  public void CanFindProjectByNumber()
{
      EDMSystem system = new EDMSystem();
system.LoginAs(UserType.Administrator);
system.CreateProject(1, "my first project");
system.LoginAs(UserType.Bob);

      Project foundProject = system.FindProject(1);

      Assert.Equal<int>(1, foundProject.Number);
      Assert.Equal<string>("my first project", foundProject.Name);
}

  //* A user can find a project by name.
  [Test]
  public void CanFindProjectByName()
{
      EDMSystem system = new EDMSystem();
system.LoginAs(UserType.Administrator);
system.CreateProject(1, "my first project");
system.LoginAs(UserType.Bob);

      Project foundProject = system.FindProject("my first project");

      Assert.Equal<int>(1, foundProject.Number);
      Assert.Equal<string>("my first project", foundProject.Name);
}

  //* A user can add a drawing to the system.
  [Test]
  public void CanAddADrawingToAProject()
{
      EDMSystem system = new EDMSystem();
system.LoginAs(UserType.Administrator);
      Project project = system.CreateProject(1,
                                    "my first project");
system.LoginAs(UserType.Bob);

project.AddDrawing("blah");

      Assert.Equal<int>(1, project.DrawingCount);
}

  //* A user can view the list of drawings for a project
  [Test]
  public void CanViewTheListOfDrawingsForAProject()
{
      EDMSystem system = new EDMSystem();
system.LoginAs(UserType.Administrator);
      Project project = system.CreateProject(1,
                                      "my first project");
project.AddDrawing("blah");
project.AddDrawing("blah too");

      ReadOnlyCollection<Drawing> drawings = project.Drawings;

      Assert.Equal<int>(2, drawings.Count);
      Assert.Equal<string>("blah", drawings[0].Name);
      Assert.Equal<string>("blah too", drawings[1].Name);
}

  // A user can filter the list of drawings by ???

  //* A user can enter (or modify) the date when their department (user) received a drawing.
  [Test]
  public void CanEnterWhenDepartmentReceivedDrawing()
{
      EDMSystem system = new EDMSystem();
system.LoginAs(UserType.Administrator);
      Project project = system.CreateProject(1,
                                      "my first project");
system.LoginAs(UserType.Bob);
      Drawing drawing = project.AddDrawing("blah");
      DateTime referenceDate = new DateTime(1979, 1, 21);

drawing.WasReceived(system.CurrentUser, referenceDate);

      Assert.Equal<DateTime>(referenceDate, drawing.Received(system.CurrentUser));
}

  // A user cannot modify when a different department's received a drawing.
  [Test]
  public void CannotEnterWhenAnotherDepartmentReceivedDrawing()
{
      EDMSystem system = new EDMSystem();
system.LoginAs(UserType.Administrator);
      Project project = system.CreateProject(1,
                                       "my first project");
system.LoginAs(UserType.Bob);
      Drawing drawing = project.AddDrawing("blah");
      DateTime referenceDate = new DateTime(1979, 1, 21);
drawing.WasReceived(system.CurrentUser, referenceDate);

system.LoginAs(UserType.Joe);

      Assert.Throws<InvalidOperationException>(delegate { drawing.WasReceived(UserType.Bob, DateTime.Now); });
}

  //* The administrator can edit all the details for a drawing. (??)
  //* A user can add drawings to the system sequentially without having to re-enter all the data each time.

  //* A user can up-rev a drawing (create a new revision by only changing the revision number and dates)
  [Test]
  public void UserCanUpRevADrawing()
{
      EDMSystem system = new EDMSystem();
system.LoginAs(UserType.Administrator);
      Project project = system.CreateProject(1,
                                       "my first project");
system.LoginAs(UserType.Bob);
      Drawing drawing = project.AddDrawing("blah");
      DateTime referenceDate = new DateTime(2007, 4, 1);

      Drawing newDrawing = project.UpRevDrawing(drawing,
                                        "A053", referenceDate);

      Assert.Equal<int>(2, project.DrawingCount);
      Assert.Equal<Drawing>(newDrawing, project.Drawings[1]);
      Assert.Equal<string>("A053", newDrawing.RevisionNumber);
}

  //* A user can edit the dates for a group of drawings for their department.
  [Test]
  public void UserCanEnterWhenDepartementReceivedASetOfDrawings()
{
      EDMSystem system = new EDMSystem();
system.LoginAs(UserType.Administrator);
      Project project = system.CreateProject(1,
                                      "my first project");
system.LoginAs(UserType.Bob);
      Drawing drawing = project.AddDrawing("blah");
      Drawing drawing2 = project.AddDrawing("blah 2");
      Drawing drawing3 = project.AddDrawing("blah 3");
      DateTime referenceDate = new DateTime(1979, 1, 21);
      List<Drawing> drawings =
                        new List<Drawing>(project.Drawings);

drawings.ForEach(delegate(Drawing d)
{ d.WasReceived(system.CurrentUser, referenceDate); });

      Assert.Equal<DateTime>(referenceDate,

                        drawings[0].Received(system.CurrentUser));
      Assert.Equal<DateTime>(referenceDate,
drawings[1].Received(system.CurrentUser));
      Assert.Equal<DateTime>(referenceDate,
drawings[2].Received(system.CurrentUser));
}

  //* The administrator can archive a project.
  [Test]
  public void AdministratorCanArchiveAProject()
{
      EDMSystem system = new EDMSystem();
system.LoginAs(UserType.Administrator);
      Project project = system.CreateProject(0,
                                      "my first project");

project.Archive(system.CurrentUser);

      Assert.True(project.IsArchived);
}

  //* The administrator can un-archive a project.
  [Test]
  public void AdministratorCanUnArchiveAProject()
{
      EDMSystem system = new EDMSystem();
system.LoginAs(UserType.Administrator);
      Project project = system.CreateProject(0,
                                      "my first project");
project.Archive(system.CurrentUser);

project.UnArchive(system.CurrentUser);

      Assert.False(project.IsArchived);
}
}

public class EDMSystem : IUserService
{
  public EDMSystem()
{
projectDictionary = new Dictionary<int, Project>();
projectDictionaryByName = new Dictionary<string, Project>();
}

  public UserType CurrentUser
{
      get { return currentUser; }
}

  public int ProjectCount
{
      get { return 1; }
}

  public void LoginAs(UserType userType)
{
currentUser = userType;
}

  public Project CreateProject(int projectNumber,

                               string projectName)
{
      Project project = new Project(projectNumber,
projectName, this);
projectDictionary.Add(projectNumber, project);
projectDictionaryByName.Add(projectName, project);

      return project;
}

  public Project FindProject(int projectNumber)
{
      return projectDictionary[projectNumber];
}

  public Project FindProject(string projectName)
{
      return projectDictionaryByName[projectName];
}

  private UserType currentUser;
  private Dictionary<int, Project> projectDictionary;
  private Dictionary<string, Project> projectDictionaryByName;
}

public class Project
{
  public Project(int number, string name,
                 IUserService userService)
{
      this.number = number;
      this.name = name;
      this.userService = userService;

drawings = new List<Drawing>();
}

  public string Name
{
      get { return name; }
}

  public int Number
{
      get { return number; }
}

  public int DrawingCount
{
      get { return drawings.Count; }
}

  public ReadOnlyCollection<Drawing> Drawings
{
      get { return drawings.AsReadOnly(); }
}

  public bool IsArchived
{
      get { return isArchived; }
}

  public Drawing AddDrawing(string drawingName)
{
      Drawing drawing = new Drawing(drawingName, userService);
drawings.Add(drawing);
      return drawing;
}

  public Drawing UpRevDrawing(Drawing drawing,
                              string revisionNumber,
                              DateTime referenceDate)
{
      Drawing upRevvedDrawing = new Drawing(
drawing.Name, userService);
upRevvedDrawing.RevisionNumber = revisionNumber;

drawings.Add(upRevvedDrawing);

      return upRevvedDrawing;
}

  public void Archive(UserType userType)
{
      if (userType == UserType.Administrator)
{
isArchived = true;
}
}

  public void UnArchive(UserType userType)
{
      if (userType == UserType.Administrator)
{
isArchived = false;
}
}

  private string name;
  private int number;
  private List<Drawing> drawings;
  private IUserService userService;
  public bool isArchived;
}

public class Drawing
{
  public Drawing(string name, IUserService userService)
{
      this.name = name;
      this.userService = userService;

receivedByUser = new Dictionary<UserType, DateTime>();
}

  public string Name
{
      get { return name; }
}

  public string RevisionNumber
{
      get { return revisionNumber; }
      set { revisionNumber = value; }
}

  public void WasReceived(UserType userType,
                          DateTime referenceDate)
{
      if (userType != userService.CurrentUser)
{
          throw new InvalidOperationException();
}

receivedByUser[userType] = referenceDate;
}

  public DateTime Received(UserType userType)
{
      return receivedByUser[userType];
}

  private string name;
  private Dictionary<UserType, DateTime> receivedByUser;
  private IUserService userService;
  private string revisionNumber;
}

public enum UserType
{
Administrator,
Bob,
Joe
}

public interface IUserService
{
  UserType CurrentUser { get; }
}

 

There are a few things I want to point out about this code:

  • It's already gone through a few refactorings. When I reached the story about disallowing users to modify other users (groups?) received date I had to introduce IUserSerivce and change a few constructors.
  • I realise my constructors can be changed with Dependency Injection from ObjectBuilder (CAB) when we reach that stage. For now, we're doing it the simple way.
  • There is no real storage mechanism. The user stories never mentioned persistence :)
  • The ubiquitous language is starting to change. The user stories are starting to change. This is a Good Thing. One of the conversions I had at Agile 2007 with the Brian Button (an incredibly intelligent and most definitely non-red individual) was about developing bottom-up with EDD versus inside-out. Along with the concepts of BDD, I'm going to try out what he introduced me to. Brian - apologies if I'm not doing things quite right .. I'm learning as I go along here :)

I'd love to hear what people think about this .. have I completely gone off the deep end with regards to EDD here? Are the unit tests I currently have better written as acceptance tests (in Behave# for instance)? Any and all comments welcome.