0

I am developing a Download Manager using with UP Design.In this Elaboration iteration, my main use case:download the file.Here is the Download.java

public class Download  implements Runnable {

// Max size of download buffer.
private static final int MAX_BUFFER_SIZE = 1024;

// These are the status names.
public static final String STATUSES[] = {"Downloading", "Complete"};

// These are the status codes.
public static final int DOWNLOADING = 0;
public static final int COMPLETE = 1;
public static void main(String[] args) throws MalformedURLException {
    System.out.println("Welcome to Download Manager.");
    System.out.println("Enter a URL.");
    URL url;
    String s;
    Scanner scan= new Scanner(System.in);
    s=scan.nextLine();
    url= new URL(s);
    Download download=new Download(url);

}

private URL url; // download URL
private int size; // size of download in bytes
private int downloaded; // number of bytes downloaded
private int status; // current status of download

// Constructor for Download.
public Download(URL url) {
    this.url = url;
    size = -1;
    downloaded = 0;
    status = DOWNLOADING;

    // Begin the download.
    download();
}
 // Start or resume downloading.
private void download() {
    System.out.println("Starting.");
    Thread thread = new Thread(this);
    thread.start();
}
// Download file.
public void run() {
    RandomAccessFile file = null;
    InputStream stream = null;

    try {
        // Open connection to URL.
        HttpURLConnection connection =
                (HttpURLConnection) url.openConnection();

        // Specify what portion of file to download.
        connection.setRequestProperty("Range",
                "bytes=" + downloaded + "-");

        // Connect to server.
        connection.connect();


        int contentLength = connection.getContentLength();


  /* Set the size for this download if it
     hasn't been already set. */
        if (size == -1) {
            size = contentLength;

        }

        // Open file and seek to the end of it.
        file = new RandomAccessFile(getFileName(url), "rw");
        file.seek(downloaded);

        stream = connection.getInputStream();
        while (status == DOWNLOADING) {
    /* Size buffer according to how much of the
       file is left to download. */
            byte buffer[];
            if (size - downloaded > MAX_BUFFER_SIZE) {
                buffer = new byte[MAX_BUFFER_SIZE];
            } else {
                buffer = new byte[size - downloaded];
            }
            System.out.print("%"+(downloaded/size)+'\r');
            // Read from server into buffer.
            int read = stream.read(buffer);
            if (read == -1){
                System.out.println("File was downloaded");
                break;
            }

            // Write buffer to file.
            file.write(buffer, 0, read);
            downloaded += read;

        }

  /* Change status to complete if this point was
     reached because downloading has finished. */
        if (status == DOWNLOADING) {
            status = COMPLETE;

        }
    } catch (Exception e) {
       System.out.println("Error!");
    } finally {
        // Close file.
        if (file != null) {
            try {
                file.close();
            } catch (Exception e) {}
        }

        // Close connection to server.
        if (stream != null) {
            try {
                stream.close();
            } catch (Exception e) {}
        }
    }
}

How can I write the test code of this code?For example should I test the URL verifying,or should I control whether the file is downloading or not?How can I do these tests? Thanks.

ntf
  • 1,323
  • 2
  • 12
  • 17
  • Your code is pretty badly testable. You should look into dependency injection and mocks, then split it up into multiple, easier to test objects. – millimoose Apr 05 '13 at 23:20
  • @millimoose How can I look at the dependency injection and mocks? – ntf Apr 06 '13 at 08:18
  • @millimoose I am new in TDD, I couldn't find how can I split it. – ntf Apr 06 '13 at 08:20
  • By "look at" I meant "google for an explanation of the concepts and apply them in your code and test". As far as splitting it goes, it's not really a matter of TDD, but of doing good OO design. One set of rules you can apply are the *SOLID principles*: http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod (That said, that's not an easy read, you might want to search for more explanations of those.) I'd also consider reading an actual book about TDD, if you're confused even about what asserts do. – millimoose Apr 06 '13 at 12:30

2 Answers2

1

The class you've written is difficult to test because it's trying to do too much. If you were to extract some of the responsibilities into external dependencies, you could end up with one class to manage the low-level downloading from URLs and another to manage the local filesystem. Something like:

interface UrlDownloader {
    InputStream download(URL url, int offset) throws IOException;
}

interface DownloadFolder {
    List<String> getFiles();
    void writeToFile(String filename, InputStream contents) throws IOException;
    void getFileSize(String filename);
}

The download manager can then be tested with mocked versions of these classes. With a library like mockito, you can write a test like:

@Test
public void canDownloadCompleteFile() throws IOException {
    URL url = new URL("http://example.com/file.txt");
    InputStream inputStream = new ByteArrayInputStream("abc".getBytes());
    UrlDownloader urlDownloader = mock(UrlDownloader.class);
    DownloadFolder downloadFolder = mock(DownloadFolder.class);
    when(urlDownloader.download(url, 0)).thenReturn(inputStream);

    DownloadManager manager = new DownloadManager(urlDownloader, downloadFolder);

    manager.download(url);

    verify(downloadFolder).writeToFile("file.txt", inputStream);
}

With mockito you can control when dependencies throw exceptions, or verify that methods are called with specific parameters.

Another way would be to create fake classes that implement your interfaces and use in-memory data structures instead of the real filesystem/network. The state of these classes can then be tested with assertEquals etc:

@Test
public void canDownloadCompleteFile() throws IOException {
    URL url = new URL("http://example.com/file.txt");
    FakeDownloadFolder downloadFolder = new FakeDownloadFolder();
    FakeUrlDownloader urlDownloader = new FakeUrlDownloader();
    urlDownloader.setUrlContents(url, "abc".getBytes());

    DownloadManager manager = new DownloadManager(urlDownloader, downloadFolder);

    manager.download(url);

    assertEquals("abc".getBytes(), downloadFolder.getFileAsByteArray("file.txt"));
}

The idea behind test-driven development is that you only write code that is backed by tests. So you'd implement enough of DownloadManager to pass the first test, and then add another test (resume incomplete download, for example).

fgb
  • 18,439
  • 2
  • 38
  • 52
0

Answering your direct question: Write a test for these code is really hard.

The beauty of unit tests is that they show you how easy the client of your code may use the module. There is a big field called "Object Oriented Analysis and Design" which intended to help programmers with such questions.

Here, you should change your code so, that there exist several routines with single responsibility, e.g. one for communicating over network, one for storing custom data stream to HDD, another for processing use input. This way you may test these 'routines' separately, even providing your own 'network' environment (for instance class that will put hardcoded values instead of really connecting to network)

Alex Turbin
  • 2,554
  • 2
  • 22
  • 35
  • Thanks for your reply, but I still couldn't find how can I write tes class for this class.For example I looked at some examples , there is a use of assertEqual().How can I use assertEqual in my situation.?Any idea? – ntf Apr 06 '13 at 08:18