A TDD Example
Tags: tdd, ruby
Published: 2019-06-26 17:27:43

---
title: A TDD Example
published: true
description: Step by step on TDD process
tags: TDD, ruby
---
# Introduction
As part of my TDD learning process I faced this problem and found hard to solve it following the TDD rules.
So I started this discussion --> https://dev.to/delbetu/solve-this-simple-problem-with-tdd-5e41

The TDD process I tried to follow at that point was:
1. Write a set of tests (input--->expected-output)
2. Pick the simplest test case to solve. (RED)(Write a failing spec)
3. Write the most naive code change that solves the current set of tests. (GREEN)
4. Check if DRY is needed (Refactor)
5. Go back to 2

## The change that made me reach the solution
Instead of creating a predefined set of test I created a new test conveniently for each TDD cycle.
1. Write a test case conveniently for the current state of the algorithm (RED)
2. Write the most naive code change that solves the current set of tests. (GREEN)
3. DRY if needed (Refactor)
4. Go back to 1

In the example we will understand what `conveniently` means.

# The Problem
```
flatten an array of arbitrarily nested arrays of integers into a flat array of integers. e.g. [[1,2,[3]],4] -> [1,2,3,4].
```
Before you continue reading try to solve it with TDD.

# Hands on!

### Cycle 1
```ruby
def test_empty
  assert_equal [], flatten([])
end

def flatten(array_tree)
  []
end
```

### Cycle 2
```ruby
def test_no_subarray_one_element
  assert_equal [1], flatten([1])
end

def flatten(array_tree)
  array_tree
end
```

### Cycle 3
The first and only element is an array.
At this point I decided decided to tackle the `recursiveness` behavior of the algorithm.
So I conveniently chose the next set of test cases.

```ruby
def test_with_subarray_one_element
  assert_equal [1], flatten([[1]])
end

def flatten(array_tree)
 if array_tree[0].is_a?(Array)
   array_tree[0]
 else
   array_tree
 end
end
```

### Cycle 4
```ruby
def test_with_subarray_one_element
  assert_equal [1], flatten([[1]])
end

def flatten(array_tree)
  if array_tree[0].is_a?(Array)
    array_tree[0]
  else
    array_tree
  end
end
```

### Cycle 5
```ruby
def test_with_subsubarray_one_element
  assert_equal [1], flatten([[[1]]])
end

def flatten(array_tree)
  if array_tree[0].is_a?(Array)
    if array_tree[0][0].is_a?(Array)
      array_tree[0][0]
    else
      array_tree[0]
    end
  else
    array_tree
  end
end
```

### Cycle 6
At this point you can recognize a recursive pattern.

```ruby
def test_with_subsubarray_one_element
  assert_equal [1], flatten([[[[1]]]])
end

def flatten(array_tree)
  if array_tree[0].is_a?(Array)
    if array_tree[0][0].is_a?(Array)
      if array_tree[0][0][0].is_a?(Array)
        array_tree[0][0][0]
      else
        array_tree[0][0]
      end
    else
      array_tree[0]
    end
  else
    array_tree
  end
end

# Refactor
def flatten(array_tree)
  if array_tree[0].is_a?(Array)
    flatten(array_tree[0])
  else
    array_tree
  end
end
```


### Cycle 7
At this point I realized of two things about the generalization of the algorithm.
(Left in comments)
These two points will make me pick the test for generalize that part of the behavior in future tests.

```ruby
def test_when_first_and_second_are_subarray
  assert_equal [1,2], flatten([[1], [2]])
end

def flatten(array_tree)
  if array_tree[0].is_a?(Array)
    result = flatten(array_tree[0]) # I will need this for each sub_array
    if array_tree[1].is_a?(Array)
      result = result + flatten(array_tree[1])
    end
    result
  else
    array_tree # This should return only the numbers not the subarrays
  end
end
```

### Cycle 8
At this point I realized how to combine the recursive calls, so I decided to refactor.

```ruby
def test_when_all_are_subarrays
  assert_equal [1, 2, 3], flatten([[1], [2], [3]])
end

def flatten(array_tree)
  if array_tree[0].is_a?(Array)
    result = flatten(array_tree[0]) # call flatten for each sub_array and concatenate results
    if array_tree[1].is_a?(Array)
      result = result + flatten(array_tree[1])
      if array_tree[2].is_a?(Array)
        result = result + flatten(array_tree[2])
      end
    end

    result
  else
    array_tree # elements without subarrays
  end
end

# Refactor --> Generalize the ifs
def flatten(array_tree)
  if array_tree[0].is_a?(Array)
    sub_arrays = array_tree.select {|x| x.is_a?(Array)}
    sub_arrays.reduce([]) do |result, sub_array|
      result + flatten(sub_array)
    end
  else
    array_tree
  end
end
```

### Cycle 9
Tackling one of the pending behaviors found in cycle 5.
The else branch should return only the numbers not the subarrays
If the first element of the input is a number but the second is a subarray then the else branch would fail.
So the next test could be:
  `[1, [2]] --> [1, 2]`

```ruby
def test_when_first_is_number_and_second_is_subarray
  assert_equal [1, 2], flatten([1, [2]])
end

def flatten(array_tree)
  result = array_tree.reject {|x| x.is_a?(Array)} # elements without subarrays
  sub_arrays = array_tree.select {|x| x.is_a?(Array)}
  result = result + sub_arrays.reduce([]) do |accumulator, sub_array|
    accumulator + flatten(sub_array)
  end
end
```


### Cycle 10
At this point I think I found the generalized solution so the next test is a complicated case.
```ruby
def test_complicated_case
  assert_same_elements [1, 2, 3, 4, 5, 6, 7, 8, 9, 0], flatten([[[1, 2, 3]], [4, [5, 6]], 7, [[[[8]], 9]], 0])
end

def flatten(array_tree)
  result = array_tree.reject {|x| x.is_a?(Array)} # elements without subarrays
  sub_arrays = array_tree.select {|x| x.is_a?(Array)}
  result = result + sub_arrays.reduce([]) do |accumulator, sub_array|
    accumulator + flatten(sub_array)
  end
end

private

def assert_same_elements(array1, array2)
  assert array1 - array2 == []
end

```

Thanks for reading,
Comments are welcome!