From behaviour to state strategy in unit testing (1/3)

When we create Software, we’d like to be sure the code works as we expect. Let our code emerge from the tests helps us to design it properly, focus on the working behaviour specified by the feature.

Let’s walk together a path starting from the assets checking the behaviour to the state, finishing using the snapshots an automated way to generate the objects used as a reference.

There are several strategies to take into consideration creating the test, so let’s review together few of them evaluating the pros and cons.

To illustrate my example, I’ll use an ad-hoc created code hosted on Github. I’d recommend you to clone it and play a bit with it. The program is quite simple. It transforms the incoming object into another one applying some transformations on the way. The main transformer changes the root object and iterates through the ‘content’ property delegating in the right transformer to complete its own transformation, easy peasy…

Ok, let’s get the code:

git clone
git checkout step1-no_state_no_behaviour
npm i

Now you have everything in place so you can run the tests:

npm t

Let’s be focus on the test for the main transformer: (‘index.test.js’)

import transformType1 from '../main/transformers/type1Transformer';
import transformType2 from '../main/transformers/type2Transformer';
import transform from '../main';
beforeEach(() => {
describe('index.js', () => {
it('transforms the incoming raw object', () => {
const rawObject = {
name: 'NAME',
content: [
type: 'type1',
value: 'prop1',
type: 'type2',
value: 'PROP2',
    transformType1.mockImplementation(jest.fn(() => 'fakeType1Transformation'));
transformType2.mockImplementation(jest.fn(() => 'fakeType2Transformation'));
    const result = transform(rawObject);
    // checking by state
    // checking by behaviour

The SUT for this unit test is included in this file (‘index.js’):

import transformType1 from './transformers/type1Transformer';
import transformType2 from './transformers/type2Transformer';
const transformers = {
type1: transformType1,
type2: transformType2,
const transformName = name => name.toLowerCase();
const transform = raw => (
name: transformName(,
content: => (
type: rawContent.type,
value: transformers[rawContent.type](rawContent),
export default transform;

So, if you check a bit the content of the test file, you will see a quite common approach creating unit tests. It basically focuses on the dependencies of the SUT. The idea is to replace those ones with doubles (mocks in this case) to simulate a set of well-defined scenarios. Once this is done, we can inject our values and create a predictable scenario. This behaviour also allows us to check our code based on its behaviour instead of on his state.

The question now is: Should we do it?

This test is quite vague. Imagine our implementation has a bug and our code is calling the methods in the wrong way (transformer 2 for type 1 and vice-versa). Is the test failing because of this?

If you are not sure and want to double check it, go to the ‘index.js’ and change the implementation of the transformers map. The run the tests again and check the results.

The answer is no, so not s very good partner here, right?

Ok, we can work a bit the test to be more specific about what should be called and how. Let’s check out the next step:

git checkout step2-no_state_better_behaviour

Now in the test, we are checking if the methods are passing the right parameters so in case the setup is not properly setup they will fail. You can try again by yourself changing the map in the code. With this extra check in the behaviour, your tests will complain if the transformers are not properly set.

The main con about this checking behaviour approach is how coupled are our tests to the internal implementation. My personal recommendation is to try to test always based on the state and only in the case that is not possible think about checks by behaviour.

We’ll move a bit our test strategy to more state based on the next posts of the series, stay tuned!