views:

196

answers:

3

I would like to create a simple downloading tool. Give it a list of urls, a target directory and hit go. It would then download those files and dump them in the target directory. Simple enough.

But, I would like to practice TDD as well. But how would you do TDD in such an application? I can see 4 main parts of application:

  • A download task (url + target name, et cetera)
  • Something that downloads the file
  • Something that writes the file to disk
  • A user interface

Can't really TDD a user interface. And the downloading and writing will most likely be done by common .net classes. Which just leaves the download task, which is a dumb container and not very interesting to TDD...

How would you TDD such an application? Or is this a kind of application where TDD doesn't really make sense?

Pointers needed. Have no clue how or where to start :p

A: 

Of course you can start by writing tests :).

  • With each new component/feature you develop ask yourself what this feature suppose to do and see if you can check some end result.
  • Use Mocks - to fake out external dependencies (i.e. save file to disk using StreamWriter).
  • When all things fail you can always write integration tests - for example check that when you call the class that downloads a file from a specific place the file is saved to disk.
Dror Helper
So I should probably try to hide the web and file system stuff behind some interfaces? And test the actual implementation of those interfaces in some integration tests?
Svish
Yes - that's one way to test around that problem, you can also check the end result - that a file was actually saved.
Dror Helper
+1  A: 

You start with the more obvious.

I guess that behind your user interface, you'll have a main controller with methods that will look like addFilePath() and hitGo(). You've got to:

  1. Mock the internet. Tests are stuff you want to run very often. You don't want them to actually download the stuff every time. So you would mock it to return a test file that will be a part of your test suite.
  2. Call addFilePath().
  3. Call hitGo().
  4. Wait for your threads to be finished doing their work.
  5. Test that your whole test file has been placed at the correct location (which should be a temp location that is auto-cleaned, you don't want your tests polluting your dev machine).
  6. You can also test that the mocked methods have been called correctly.

When looking for a starting point in TDD, you don't have to look very far. If you do, it probably means that you don't even know what your application is supposed to do.

With the first test written, more cases should present themselves to you, allowing you to write more tests.

Virgil Dupras
+4  A: 

You can TDD user interface heavy applications if you seperate the logic out of the user interface. This is MVC in a nutshell. Here is one conceptual way of doing it expressed as class-diagram (with the model omitted):

+----------------------+ 1
|     MyDownloadUI     +--------------+
+----------+-----------+              |
           |                          |
           | implements               |
           v                          |
+----------------------+              | 1
|     {interface}      |1   1+--------+------------+
|     DownloadView     +-----+  DownloadController |
+----------------------+     +---------------------+

The only thing you need to do on the user interface is to implement a DownloadView interface and have a reference to DownloadController on where it should send it's actions to. The DownloadController should only have a reference to the DownloadView interface whenever it needs to manipulate the UI (more specifically the view). The constructors should look something like these:

//Sample of MyDownloadUI

DownloadController controller;

public MyDownloadUI {
    this.controller = new DownloadController(this);
    //...
}

//Sample of DownloadController

DownloadView view;

public DownloadController(DownloadView view) {
    this.view = view;
    //...
}

This way, the UI can be changed without the controller to worry about how the view looks or what the names of all the labels and lists are.

This has the benefit that you can TDD the logic in the download controller and have a mock that replaces the UI.

To test the actual UI you don't really do unit tests per se, it'll be more a functional test because MyDownloadUI is tightly coupled with DownloadController (unless you make an interface for the DownloadController). For a small project like this, you can pretty much just do manual smoke testing whenever you change the UI or wire something new to the controller.

Whenever you feel like a class is starting to get too much, you always have the option of breaking the logic out to another class (which makes TDD a lot easier). You've already given examples, e.g. DownloadTask, which is clearly a model class, so it's a good start. Then you have the FileDownloader which sends a DownloadedFile to a FileWriter.

The easiest implementation of DownloadController that I could think of is just one method:

  • goDownload(List<string> urls) that the MyDownloadUI calls when it wants to start downloading

Another one would be:

  • addUrl(string url) adds an url to the downloadcontroller's internal list
  • clearUrls() removes all urls in the internal list
  • goDownload() which takes the list of urls and starts the "download process"

There are a lot of TDD tutorials out there, my favorite is the video on dnrTV with Jean Paul Boodhoo (Part 1, Part 2). There is a lot to take in, but it shows a lot how to do it in practice.

Spoike
Brilliant. Will have a look at those videos. Stuff became quite a bit clearer now :) (How did you make that awesome chart? By hand?)
Svish
@Svish Yes, I made that piece of class diagram by hand. Isn't ASCII art great? :)
Spoike
@Spoike, thanks for including the video links. Will check them out!
ShaChris23