Avoiding Test Data Mutation

October 24, 2017
ruby test rspec tdd

Say we have a service object, with a dependency injected. The service performs an operation with data obtained from the dependency. For simplicity in this example, let’s say the service would wrap the data into a response hash, with keys success indicating successful operation and data containing the result of the operation.

When writing tests for such an object, it’s common to mock the dependency using Rspec and stubbing the response from the dependency. The test then creates a new service object with this dependency injected and assert the result of the operation against the mock data.

This test would pass with the current implementation of Service. But what would happen if the operation performs unintended mutation on the data received from the dependency?

So now instead of returning data {a: 1, b: 2}, the service would return {a: 1, b: 2, hello: ‘world’}. The test should fail, right? Surprisingly, at least to me and our team at first, the didn’t catch this error. Running the same test would still result in a passing test.

The reason for the passing test is this line in the test:

expect(result[:data]).to eql(data)

By asserting the test result against the variable, we are comparing the result with the object referenced by the variable data. Remember that data was the stub response returned by the dependency. This response, in turn, was mutated by the service through merge!. Throughout this process, it is always the same data object that is being operated on. When the time comes for the test to assert result[:data], it compares it to the object data, which by now have been added with a new key value pair hello: 'world'. result[:data] and data now have exactly the same content, and the test passes.

So how do we write a better test? Simply assert against the actual values.

expect(result[:data]).to eql(a: 1, b: 2)

This will ensure the test fails when that happens.

Stubbing Dependencies in Go

A quick guide to stubbing dependencies in Go
software development go test tdd

When to Use Ruby Threads?

Ruby threads are useful for performing remote operations such as HTTP requests, less for performing heavy code executions.
software development ruby threads mri global interpreter lock gil concurrency

Test Driven Development Exercise for Starters

An exercise on test driven development for beginners. Create a fully functioning class from scratch with tests written at every step.
tdd test-driven-development agile javascript