Tag Archives: c#

Unit test verification using StringBuilderStream

UPDATE!
Thanks to reddit user Porges, I now know that you *can* access the underlying buffer of a memory stream after it’s disposed. Use the ToArray or GetBuffer methods.

I recently had to write a unit test for a method that called the following private function:


private async Task WriteStreamToFileAsync(Stream stream, string filePath)
{
  // FileSystem.OpenFile is a wrapper for System.IO.OpenFile...
  using (var fileStream = FileSystem.OpenFile(filePath, FileMode.CreateNew))
  {
    await stream.CopyToAsync(fileStream);
    await stream.FlushAsync();
  }
}

The FileSystem object is a very thin wrapper around the System.IO.* methods of the same name. This allows a Mock to be injected for the unit tests. During the Setup() for the unit test, I new’d up a class level MemoryStream object and had it returned by the Mock call to OpenFile when the FileMode passed in is set to CreateNew. The goal here is to verify what was written to the MemoryStream after each unit test.

     private MemoryStream writableStream;
     private Mock<fileSystem> fileSystem;
     [Setup]
     public void Setup() {
         writableStream = new MemoryStream();
         fileSystem= new Mock<fileSystem>();
         fileSystem.Setup(f => f.OpenFile(It.IsAny<string>(), FileMode.CreateNew))
                .Returns(writableStream);
         //... other setups omitted.
     }

Then I wrote some unit tests that called the method being tested (which subsequently called the WriteStreamToFileAsync function that writes to the stream), followed by the verification. The verification consisted of seeking back to the start of the stream, and then reading the stream to verify its contents.

However when I actually ran the test, it threw an exception stating that the stream was closed. This makes sense after inspecting the method that uses the stream. It’s wrapped in a Using statement: of course the stream would be closed since its being disposed. Quite inconveniently, Microsoft decided to stay true to the philosophy of the Dispose method and does not allow the stream to be reopened. I haven’t looked under the hood, but I’m guessing any memory allocated for storage is released upon disposal. Bummer.

We could modify the WriteStreamToFileAsync function, but changing production code to conform to unit tests is bad for your production code (in this case, a potential memory leak) and creates brittle tests. Another option is to feed the unit test a stream that writes to a file on disk, then the verification could read the file to assert its contents. That will work, but it would really slow down the unit tests and introduce an unnecessary external dependency. What’s really needed is a stream that stores data in memory, but still exposes its underlying data storage mechanism after being disposed.

With those requirements in mind, I wrote StringBuilderStream. As the name implies, it uses a StringBuilder object as the underlying storage mechanism. The data I was trying to verify was text, so using a StringBuilder for storage has the added benefit of giving me a string that I could immediately verify without pulling it out of a stream. Also, a StringBuilder will automatically grow as needed, just like a MemoryStream object. (The full gist for StringBuilderStream is at the bottom of this post.)

Now using the StringBuilderStream, here’s my working unit test:

     private const string testString = "ipsum lorem blah blah blah...";
     private ObjectToBeTested objectToBeTested;
     private StringBuilderStream writableStream;
     private Mock<fileSystem> fileSystem;
     [Setup]
     public void Setup() {
         writableStream = new StringBuilderStream();
         fileSystem= new Mock<fileSystem>();
         fileSystem.Setup(f => f.OpenFile(It.IsAny<string>(), FileMode.CreateNew))
                .Returns(writableStream);
         //... other setups omitted.
         objectToBeTested = new ObjectToBeTested(fileSystem.Object);
     }
     [Test]
     public void WriteToFile() {
        objectToBeTested.WriteToFile(testString);
        // you don't need to seek to the beginning, just use the overloaded ToString method....
        Assert.That(writableStream.ToString(), Is.EqualsTo(testString));
     }

A few notes:

  • You should still dispose of a StringBuilderStream object; I didn’t include the teardown for brevity.
  • The StringBuilderStream does support seeking and reading via the standard Stream methods.
  • If you seek to a spot other than the end and start writing, everything from that point to the end of the StringBuilder is erased. I know that’s not typical Stream behavior but it was a shortcut to save time. I may change that later but I don’t see a lot of use cases for seeking inside a StringBuilderStream.

Here’s the gist for StringBuilderStream: