File System Interaction

8th Light Apprenticeship - Day 95

Today I have been interacting with the file system a lot. The first test I tackled was the combined Post-Get-Put-Get-Delete-Get. This meant I had to implement the ‘proper’ behaviour for each method, rather than just return a valid status.

Post Get Put Delete

Tests for interacting with the file system can be a little tricky. I didn’t want to create files and load them as fixtures. Instead, I used the JUnit Rule TemporaryFolder which I learnt about at an Agile Development course at my old job. It can create a temporary directory structure for you, and it cleans up after itself. During this task I’ve also used another rule, ExpectedException, to ensure that the error messages thrown from my custom exceptions are under test.

I started with implementing a lookup that a GET request could use. For this test, the resources are files, which contain strings, therefore I used BufferedReader, reading each line at a time and concatenating them together. This worked great. I did make an assumption though. If you look up a resource that doesn’t exist, what should the server do?

I thought about my options - Throw an exception, return empty string, return null. I don’t like seeing ‘null’ scattered across a code base, however in this case, I don’t want empty string, as that would actually put an empty string into the body of the http response, which I felt was incorrect. It would not be clear whether a resource did exist and was just empty, or whether it did exist and was null. I could have thrown an exception, but I don’t want the server to crash, so it would be swallowed. Therefore I went for the null option, but wrapped it in a method to give it context.

One point of confusion was that the test gets a resource (which doesn’t exist initially), then creates it using a POST, but the test case wants a status of 200. Looking at the spec, it states if POST actually creates a resources it should be 201. As the aim of the task is to get the tests passing, I updated my server to return 200.

Redirect

Once I had finished that test, I started on the next, redirect. Redirect returns a 302 status code, which means the requested resource resides temporarily under a different url (which was provided in the test fixture). This test was very quick to implement. That was lucky, as I had spent more time than I thought I would on the first one of the day.

Image Content

The last test I had pencilled in for today was image content. I started off, as usual with writing a test to incorporate the new route. Then, I used the file reader I had created this morning, and a whole lot of little symbols filled my terminal. The code was reading the image as a string and not particularly happy about it! I looked into how to read bytes and went into spike mode, so that I knew how to do it.

After a lot of trial and error, I came to realise that the HttpRequest I have, whose body was stored as a string (as that satisfied all the requirements I had so far), would probably be better off being stored as a byte[]. After all, I may not know if the resource I’m looking up is a text file, xml file, image and so on. The body was always being transmitted as bytes in the end, so I decided to transform the body earlier and found a lovely helper method for this, which enabled me to delete around 4 methods in a class:

       Files.readAllBytes(Paths.get(filename(resourcePath)));

As a bonus, this worked for reading text files and images as bytes from the file system, which meant I wouldn’t need to add a new method to the FileFinder interface.

Once I could see the route I needed to take, I created a new branch and TDD’d the necessary changes. I remembered Kent Beck’s quote make the change easy then make the easy change so I started with updating the request to store a byte[] for the body, checked everything worked ok, then, in a separate commit, added the new feature.

The other thing I learnt today was how useful some debugging statements are when the server is running against the fitnesse test suite. For now, I am using system.out to print to the console, but I have a note to remove them before the customer demo. The most useful information is seeing the request that is coming in, and the response that is sent back. I may keep these but use log4j, which would be a more elegant solution.

The three tests I had scheduled in for today are green, however I have a few bullet points of refactoring I want to do. I’ll save this for tonight, and start the next tests in the morning.

Example Responses