Add new song is added to song list

The add window can now be opened and closed, and its song title is initialized with an empty string. There seems to be nothing left but to make sure the song is added to the song list in the songs window when clicking the ok button. It sounds big and scary, but I don't think I can postpone it.

So I write the following test:

[Test]
public void Add_new_song_is_added_to_song_list()
{
Application
.Start(view =>
{
view.ClickAddButton(addView =>
{
addView.ChangeSongTitleTo("Sunday Morning");
addView.ClickOkButton();
});
Assert.That(view.Songs, Is.EqualTo( new []
{
new Song("Sunday Morning")
}));
view.ClickCloseButton();
});
}

I open the add song window, enter a song title, click ok, and then verify that the song list contains a song with the given song title.

For java developers, the parameter to the Is.EqualTo() method might look a bit funny. What I do is that I create an array, just like it is done in java, but I don't specify the type of the array. Again, it is the type inference that does its magic. Since the variable inside the initializer is of the type Song, the compiler assumes the array is the type Song[]. This, of course, requires that, if there are more than one item in the list, all are of the same type.

As you notice, the test contains the string "Sunday Morning" twice, once for entering the song title in the text box and once as part of the expected value in the assertion. One might argue that this is duplication and that I should introduce a constant for the two.

I wouldn't complain loudly if you would do that, but I prefer not to. Like before, my argument is readability. For me, having the string stated twice makes the test easier to read. If I introduce a constant I would have to look elsewhere for the definition. Even if I define it as a local constant, that is, in the test method, I would have to move my eyes a few lines up to see what it means.

Also, this kind of duplication is not a dangerous one. Dangerous duplication is when you have something that must or ought to be the same in different types, files and/or projects. In such situations it is not obvious that a change to one of the copies requires you to change some other copy as well. Here, the two copies are so close that it is hard to miss. And even if you accidently change one, the test will quickly remind you to change the other.

One could even argue whether this is actually duplication. Duplication is about concepts, not about code. That is, you could have the exact same string, or code, in two places that actually mean different things. Say, for instance, that the width of your button is 100 pixels, and that the hight of an image in the same dialog also happens to be 100 pixels. Just because you have the number 100 specified in two places doesn't mean you have duplication.

This, I admit, is an extreme example, but I argue that my point is valid. In my test, the situation is a bit less obvious. The first occurence of the string specifies what the user types as the song title and the second what to expect in the songs list. I expect them to be the same, but that doesn't mean they represent the same concept.

After this detour, it is time to make the code compile. So I add the ChangeSongTitleTo() method to the view fake.

public class AddSongViewFake : IAddSongView
{
// ...
 
public void ChangeSongTitleTo( string songTitle)
{
}
}

The question is, what kind of code should I add to the ChangeSongTitleTo() method? Previously, in the methods that triggers a button click, what I did was to raise an event and let a listener handle it. That is, when clicking the close button, for instance, the view doesn't close the view, but passes on that responsibility to the code behind. In the same way, I would prefer if the view doesn't hold on to the song title, but lets the code behind handle that. When the user changes the text, the view should notify the code behind about it and the code behind should update the view accordingly.

However, that's not how the WinForms Textbox control that I'm using works. It keeps track of its own state. When the user types the keyboard, the control updates its internal state and then it passes on an event to listeners saying that it has changed. That is, the control doesn't ask the listeners to make changes to it, it tells the listeners that it has already changed its state.

Should I take advantage of the fact that the view knows its state, and not store the state in the code behind? Whatever choice I make, I want the fake to work just like the real view. And since the real Textbox keeps track of its content, the fake view should also hold on to the song title. So I add that behavior to the fake.

And to keep things simple for now, I don't move the state into the code behind for now, but only keep it in the view. And therefore, I don't even have to notify the code behind that it has changed.

public class AddSongViewFake : IAddSongView
{
// ...
 
public void ChangeSongTitleTo( string songTitle)
{
SongTitle = songTitle;
}
}

To make the code compile, I also add a constructor to the Song class.

public class Song
{
public Song( string songTitle)
{
}
}

Now I can finally run the test.

SongsFixture.Add_new_song_is_added_to_song_list : Failed
Expected is <SongLibrary.Song[1]>, actual is <System.Collections.ObjectModel.ReadOnlyCollection`1[SongLibrary.Song]> with 0 elements
Values differ at index [0]
Missing: < <SongLibrary.Song> >

The test fails, as expected, but the failure message is not the best. I understand that there is a Song missing in the songs list, but the information about what is missing is really bad. What happens here is that NUnit outputs the information using the Song.ToString() method, and since I haven't created such method, the default (on the class Object) is used which only outputs the name of the class.

So to improve the error message, I override the ToString() method in the Song class.

public class Song
{
public string SongTitle { get; private set; }
 
public Song( string songTitle)
{
SongTitle = songTitle;
}
 
public override string ToString()
{
return string .Format("song title: '{0}'", SongTitle);
}
}

To get a good message, I obviously have to store the song title, and I use an auto property for that.

The string.Format() method requires some explanation. It is like javas printf() methods, but the parameter placeholders are specified with {N} instead of %N (where N is the index of the parameter).

As you can see, I have put the {0} with quotes. This is a nice trick to use for strings since it makes it easy to distinguish empty strings from strings with only whitespace in it.

Now I run the test again.

SongsFixture.Add_new_song_is_added_to_song_list : Failed
Expected is <SongLibrary.Song[1]>, actual is <System.Collections.ObjectModel.ReadOnlyCollection`1[SongLibrary.Song]> with 0 elements
Values differ at index [0]
Missing: < <song title: 'Sunday Morning'> >

The failure message is much clearer.

To make the test pass, I need to do something in the Ok button handler. This is what it currently looks like.

public class AddSongShower
{
// ...
 
public void Show()
{
View.SongTitle = string .Empty;
View.OnOkButtonClick += OnOkButtonClick;
View.OnCancelButtonClick += View.CloseView;
View.ShowView();
}
 
private void OnOkButtonClick()
{
View.CloseView();
}
}

The simplest way I can think of to make this test pass is to pass the SongsShower into the AddSongShower and use that as a mediator to update the song list. So that is what I do.

public class SongsShower
{
// ...
 
private void OnAddButtonClick()
{
AddSongShowerFactory.Show( this );
}
}
 
public class AddSongShowerFactory
{
private IAddSongViewFactory AddSongViewFactory { get; set; }
 
public void Show(SongsShower songsShower)
{
var view = AddSongViewFactory.Create();
new AddSongShower(view, songsShower).Show();
}
}

I pass the SongsShower into the Show() method of the factory which, in turn, passes it on to the AddSongShower. Unfortunately, I add a dependency from the AddSongShowerFactory to the SongsShower only for it to be able to pass on the parameter. But since it is a factory, it's not that bad.

Now the AddSongShower can add the song.

public class AddSongShower
{
private IAddSongView View { get; set; }
private SongsShower SongsShower { get; set; }
 
public AddSongShower(IAddSongView view, SongsShower songsShower)
{
View = view;
SongsShower = songsShower;
}
 
// ...
 
private void OnOkButtonClick()
{
SongsShower.Add( new Song(View.SongTitle));
View.CloseView();
}
}

I retrieve the song title from the view and use it to create a new song which I pass on to an Add() method in the SongsShower. And here is my implementation for that method.

public class SongsShower
{
// ...
 
public void Add(Song song)
{
View.Songs = new ReadOnlyCollection<Song>( new List<Song>( new [] { song }));
}
}

That was a lot of code. Let's see where it gets me.

SongsFixture.Add_new_song_is_added_to_song_list : Failed
Expected is <SongLibrary.Song[1]>, actual is <System.Collections.ObjectModel.ReadOnlyCollection`1[SongLibrary.Song]> with 1 elements
Values differ at index [0]
Expected: <song title: 'Sunday Morning'>
But was: <song title: 'Sunday Morning'>

What? They look the same, but yet the test fails. And the reason is obviously that I do the comparison using reference equals rather than using value semantics. That is, the Equals() method that the Song class inherits from object is used, so in order to make the test pass I need to implement an Equals() method that checks the song title.

public class Song
{
// ...
 
public bool Equals(Song other)
{
if (ReferenceEquals( null , other)) return false;
if (ReferenceEquals( this , other)) return true;
return Equals(other.SongTitle, SongTitle);
}
 
public override bool Equals( object obj)
{
if (ReferenceEquals( null , obj)) return false;
if (ReferenceEquals( this , obj)) return true;
if (obj.GetType() != typeof (Song)) return false;
return Equals((Song) obj);
}
 
public override int GetHashCode()
{
return SongTitle.GetHashCode();
}
}

I have two Equals() methods, one for when I compare my instance with an instance that the compiler knows is of the type Song, and one where the type is unknown. Both first check if the instance that I compare against is null and then if the two instances are actually the same instance (that is, they reference the same object). Finally, I compare the song title of the two instances with each other.

Also, I have added a GetHashCode() method. A common way to compare two instances is to calculate the hash code of the two instances and, only if they are equal, use the Equals() method for further comparison. Calculating the hash code is typically much faster than using Equals(), so it is done as an optimization. Most notably, the Dictionary class use it in this way. Therefore, it is important to override GetHashCode() everytime you override Equals() and make sure that two instances that are equal always return the same hash code.

Now when I run the test it is finally green. And running the application, adding a song works great.

There are many things I don't like about the code I just wrote though, so I write a few todo index cards.

  • Adding another song won't work since when I set the Songs property on the view I only include the newly added song.
  • There is a circular dependency between the SongsShower and the AddSongShower (through the AddSongShowerFactory). That is, the SongsShower depends on AddSongShower and the AddSongShower depend on SongsShower.
  • I don't think the Song class should use value semantics for comparison.

Before I start taking care of my todos, I check in my code.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>