Table-driven tests

While studying up on some data structure and algorithm fundamentals, I learned of a novel approach to writing tests for my code. Table driven tests are a method of writing unit tests where multiple cases are defined in a structured format. Testing framework not required.

If I were to use this code in a real project I would extract the tests into a separate file and use a testing framework. But for now, while tinkering and learning, this is a great way to test code.

I believe this qualifies as table-driven testing. Running the same set of tests with different inputs and expected outputs, organized in a table-like structure.

eg:

$ ruby recursive-algorithms.rb
"#quicksort! | [0, 1, 2, 3, 5, 6] = [0, 1, 2, 3, 5, 6] | true"
"#quickselect! | 10 = 10 | true"
"#greatest_product_of_three_numbers | 90000 = 90000 | true"
"#find_missing_number | 3 = 3 | true"
"#find_missing_number | 8 = 8 | true"

My ruby file recursive-algorithms.rb:

# Quicksort.

class SortableArray
  attr_reader :array

  def initialize(array)
    @array = array
  end

  def partition!(left_pointer, right_pointer)
    pivot_index = right_pointer
    pivot = @array[pivot_index]
    right_pointer -= 1

    while true
      while @array[left_pointer] < pivot do
        left_pointer += 1
      end

      while @array[right_pointer] > pivot do
        right_pointer -= 1
      end

      if left_pointer >= right_pointer
        break
      else
        @array[left_pointer], @array[right_pointer] = @array[right_pointer], @array[left_pointer]
        left_pointer += 1
      end
    end
    @array[left_pointer], @array[pivot_index] = @array[pivot_index], @array[left_pointer]
    return left_pointer
  end

  def quicksort!(left_index, right_index)
    if right_index - left_index <= 0
      return
    end

    pivot_index = partition!(left_index, right_index)
    quicksort!(left_index, pivot_index - 1)
    quicksort!(pivot_index + 1, right_index)
  end

  def quickselect!(kth_lowest_value, left_index, right_index)
    if right_index - left_index <= 0
      return @array[left_index]
    end

    pivot_index = partition!(left_index, right_index)

    if kth_lowest_value < pivot_index
      quickselect!(kth_lowest_value, left_index, pivot_index - 1)
    elsif kth_lowest_value > pivot_index
      quickselect!(kth_lowest_value, pivot_index + 1, right_index)
    else
      return @array[pivot_index]
    end
  end
end

def greatest_product_of_three_numbers(array)
  sa = SortableArray.new(array)
  sa.quicksort! 0, array.length - 1
  return sa.array[-3] * sa.array[-2] * sa.array[-1]
end

def find_missing_number(array)
  sa = SortableArray.new(array)
  sa.quicksort! 0, array.length - 1
  sa.array.each_with_index do |value, i|
    if i != value
      return i
    end
  end
end

# TESTS

[
  [[0, 5, 2, 1, 6, 3], [0, 1, 2, 3, 5, 6]]
].each do |test, expected|
  sa = SortableArray.new(test)
  sa.quicksort! 0, test.length - 1
  pp "#quicksort! | #{sa.array} = #{expected} | #{sa.array == expected}"
end

[
  [[0, 50, 20, 10, 60, 30], 10]
].each do |test, expected|
  sa = SortableArray.new(test)
  ret = sa.quickselect! 1, 0, test.length - 1
  pp "#quickselect! | #{ret} = #{expected} | #{ret == expected}"
end

[
  [[0, 50, 20, 10, 60, 30], 90000]
].each do |test, expected|
  ret = greatest_product_of_three_numbers(test)
  pp "#greatest_product_of_three_numbers | #{ret} = #{expected} | #{ret == expected}"
end

[
  [[5, 2, 4, 1, 0], 3],
  [[9, 3, 2, 5, 6, 7, 1, 0, 4], 8]
].each do |test, expected|
  ret = find_missing_number(test)
  pp "#find_missing_number | #{ret} = #{expected} | #{ret == expected}"
end