TDD Basics : Using Factorial kata to illustrate Transformation Priority Premise

Objective


To learn how to apply Transformation Priority Premise to Factorial problem

Steps

Step 1

Create test_factorial.rb with :

class TestFactorial < MiniTest::Test
  def test_zero_factorial_is_one
    result = Factorial.calculate(0)

    assert_equal 1, result
  end
end

Run the test:

$ruby -rminitest/pride test_factorial.rb --verbose

Step 2

Add the Factorial class to the top of the file.

class Factorial
  def self.calculate(n)
  end  
end

The test fails.

Step 3

Return hard coded value.

class Factorial
  def self.calculate(n)
    1
  end  
end

Now the test passes.

Step 4

Add the second test:

def test_one_factorial_is_one
  result = Factorial.calculate(1)

  assert_equal 1, result
end

This test passes without failing.

Step 5

Add the third test.

def test_two_factorial_is_two
  result = Factorial.calculate(2)

  assert_equal 2, result
end

This test fails.

Step 6

Change the factorial implementation:

if n < 2
  1
else
  n
end

The test now passes.

Step 7

Add the fourth test.

def test_three_factorial_is_six
  result = Factorial.calculate(3)

  assert_equal 6, result
end

Step 8

Change the Factorial implementation:

if n < 2
  1
else
  result = 1
  begin
    next_number = result + 1
    result *= next_number
  end while(next_number < n)
  result
end

The result we get 42 is wrong. We expect 24. Let's analyze our code.

Run 1 :

result = 1
next_number = 2
result = 2
next_number < n (2 < 4) yes

Run 2 :

next_number = 2 + 1 = 3
result = 2 * 3 = 6
next_number < n ( 3 < 4) yes

Run 3 :

next_number = 6 + 1
result = 6 * 7 = 42

The problem is that we need to increment the index into an array. Don't do next_number = result + 1 (finding the next number is not the same as the intermediate result + 1)

Step 9

Change the Factorial implementation:

  if n < 2
    1
  else
    result = 1
    for i in (1..n).to_a
      result *= i
    end
    result
  end

The test now passes.

Step 10

Let's cleanup.

result = 1
for i in (1..n).to_a
  result *= i
end
result

The following implementations will also work.

result = 1
  1.upto(n) do |i|
  result *= i
end
result
result = 1
(1..n).to_a.each do |i|
  result *= i
end
result

Summary


You can see how the code evolved from no code at all where it was returning nil to hard coded value of 1. Then we had a split in the execution path by using the if-else statement. Then the else block was generalized to handle larger numbers and eventually we got rid of the if-else statement. The challenge is picking the transformations on top of the Transformation Priority Premise list.


Related Articles


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.