July 18, 2016


Testing Generated Binary Files with RSpec

Consulting & Advisory Digital Transformation Software Development

RSpec has become the standard framework used to test code written in Ruby. Its extensive library allows developers to isolate, extract, and test specific files within an application in order to decrease bugs and improve overall quality and stability. Although RSpec provides the tools necessary to test virtually every aspect of an application, some developers may not be familiar with certain time-saving techniques that can be applied during the testing process. This article focuses on using RSpec to generate and then test PDF, XLS, or any other binary files from a Rails application.

 

Testing content extracted from such files is extremely difficult and, in the case of images (i.e., JPG files), essentially impossible. For every single file format (PDF, XLS, etc.), a separate library is needed. Writing tests this way can be very complicated and time-consuming. These files are located beyond code boundaries and are untestable without a library to load the content back into the application. Developers might choose to write a new library for their specific needs, but this task is also difficult. The methods I describe below allow developers to test any binary file without using additional libraries, including JPG images. All that is needed is an “approved” binary file and a unit test that will compare generated content with approved content.

 

As detailed in this article, testing generated binary files consists of four basic steps:

 

  1. Write test helper
  2. Implement requested functionality
  3. Generate a fresh file with #update content
  4. Build and test XLS file against generated file

 

1. Write Test Helper

 

Here is the code for Rspec helper + test:

 

Utility Class:

 

 

Be very careful not to remove the exception: “Do not commit tests with this statement.” Your tests can still deliver false positives, even though errors in the code may exist.  

 

Here is a simple test fragment you can apply my technique to:

 

 

2. Implement Requested Functionality

 

To illustrate this step, imagine your client is an online bookstore that accepts payments directly from individual customers. You have a RubyOnRails application that can accept user payments, query 3rd party APIs, and collect payment API responses. Several Rails models related to these functions are available on your server:

 

 

Now let’s say the bookstore/client requests additional functionality. They want an XLS file that includes all payments received, and they want this file to be generated every day after midnight. Your task is to produce this file and send it to the client. How do you create a testable design? By building your functionality from top to bottom. Before you begin, I recommend decoupling the file generation from your application to simplify testing.

 

First, we need a public interface to generate the XLS file:

 

 

This service can be added to the cron scheduler. We can then submit the file with fake data just to pretest the submission.

 

3. Generate a Fresh File with #update Content

 

In order to generate the file with real data, we need to add some classes with the requested functionality. These classes should be tested separately to avoid future problems in the end file:

 

  1. Payments collector can be encapsulated inside the query object.
  2. Payments builder can extract payment details and create an array with plain old Ruby objects (PORO).
  3. XlsContent builder can implement an XLS file content builder capable of consuming this payment array with PORO.

 

We could collect payments and build XLS content directly from the payment model, but doing so would make our tests unnecessarily complex. The following is more testable:

 

 

In the above class, we can collect payments and the test call method. Several unit tests would then be sufficient.

 

4. Build and Test XLS File Against Generated File

 

Our next step is to transform the array of payments into an array of PORO:

 

 

The above builder is also easy to test. You would pass payments inside Ruby and then examine #build output. You can also easily write unit tests for this array. With PORO, you can then decouple the file generator from the application and test the generated file with fake data:

 

 

You can easily test the above builder as well. You would create the array with fake data, generate content, and store it to file. Then you compare the stored content with the generated content:

 

 

The service itself can be tested as a blackbox. To execute a high-level integration test, you create several payments with specific dates. Then you pass these payments to the service and check the content of the file produced.

 

Following the steps outlined in this article can solve a significant problem: You can control your generated files. This method also allows you to create as many “approved” files as you need to cover your solution and deliver an overall high quality, stable solution. However, this standard testing method does not simplify the process of debugging files whose content can’t be viewed (PDF, XLS). It does not eliminate the need to pay close attention to detail. You can still create files with no content which will result in your subsequent tests delivering false positives. You need to manually check each file before testing to avoid these problems.

Consulting & Advisory Digital Transformation Software Development

Latest Insights in Consulting & Advisory

The Rise of Kotlin – Moving Away from Java for Android Development

Kotlin is a programming language for the Java Virtual Machine that’s able to be used in any scenarios that currently…

Introducing our Sphere Heroes Program – Artem Korenev – Employee of the month

At Sphere, employee recognition is a key component of our corporate culture. We believe in celebrating the successes of our…

Write For Sphere

Are you a writer with tech expertise? Then we want to hear from you! Here are a few guidelines for…

View All Articles arrow

We are here to help:

checkmarkto become a customer checkmarkto become an investor checkmarkto send a media inquiry checkmarkto join our team checkmarkto simply say ‘hi’
Get in Touch