Tuesday, May 18, 2010

TDD Question

Working on my music project and I decided that I needed a more direct tool to rename my mp3 files. The best way to do that is to read the tagging info embedded in the file and name and place the file in the right location on the media server. The idea is to eliminate as much manual copying/naming as possible.

One thing I realized was that it wasn't Pollux that named the files, I made that decision myself and had MediaMonkey name them only by title. And that mistake was documented below.

I knew the basic premise of how mp3 files work, but I needed more specifics. I googled mp3 tags and thought about writing my own extraction program, but why redesign the wheel when I figured there had to be some libraries out there.

I also figured this would be a good chance to explore visual studio 2010. Since 90% of the jobs in North East Wisconsin are .net, its best to keep those skills current. Although there is a certain romance to do it in Ruby or some other new language, but I don't want to spend any more time on this.

The first library I found seemed like it was going to work, but it didn't update all the tags. It also wasn't open sourced so I couldn't update to do the things I wanted. So I thought I'd use Reflector to see if I could gleam anything useful. Taking a look at the code I knew it wasn't going to work for me.

The next library I found was on source forge called, C# ID3 Library not only did this include the source, but it was wrapped within a couple of dialogs to use as examples. It even had some test projects.

I started writing my own tests to extract the file's location. I abstracted the reading of the tags into its own file, passing the file's location. I then created the new file name along with its new location doing some string manipulation.

I did some Mocking with Moq with NUnit and using an interface I was able to mock the object getting the correct tags and creating the right file path.

I then wanted to wrap the whole process within a facade. As I developed my routines, I wrote unit tests to make sure each function behaved as predicted by the tests. But then I realized that I didn't want the facade to have the dependency of the Mp3file class that reads the file.



So I wanted to make Tag an Internal class so that only the facade can access it. As I'm typing this I should make an ITag interface file to access the Tag.cs and abstract that out. That way if I want to reuse it, then all I need to do is call the interface.

Anyways as I was refactoring last night I realized that I'd have to add attributes to allow my test project access my internal classes so I could keep my unit tests. My thought however was to write a unit test around the facade method RenameMP3File.
But this got me thinking, if I did that then I would lose the ability to mock the MP3Tags object. The Tags.cs constructor instantiates the MP3Tags file with the file name and fills the properties I need. The facade is doing a great job of hiding this functionality, but now I can't mock my MP3Tags object.

So I have two options that I can think of, exposing the Tags class so I can continue to use my unit tests as already configured. Or change my unit tests to test only the facade method, but losing the ability to mock my object up thus my tests are brittle. I suspect there is a third way, but I think I'm missing it.

I know in the end that this is a small project and won't expand much, but I'm using as an exercise to try to keep my skills up and active since I'm looking for work. While I know I can get this to work, I don't want to fall out of the TDD methodology to slap it in to work. I know this changed when I added the facade logic outside of TDD, but then I would have had to add that dependency up the line. Is that a bad thing?

Well any thoughts you have would be appreciated.

1 comment:

  1. You might want to expose the Tags class only to the current namespace. Your tests should have access to that namespace.

    I have long since given up trying to keep things perfectly private. Too much privacy inhibits testability.

    ReplyDelete