Skip to content

Instantly share code, notes, and snippets.

@alexch
Forked from ryanb/quiz.rb
Created May 15, 2009 22:19
Show Gist options
  • Save alexch/112466 to your computer and use it in GitHub Desktop.
Save alexch/112466 to your computer and use it in GitHub Desktop.
# COMMUNITY CHALLENGE
#
# How would you test this Quiz#problem method? Only two rules:
#
# 1. The tests should fail if any part of the application breaks.
# For example: If "gets" is moved before "puts" then the tests should
# fail since that breaks the application.
#
# 2. You cannot change the Quiz class. But you can use whatever framework
# and tools you want for the tests. (RSpec, Cucumber, etc.)
#
# Note: The first rule used to be "no mocking" but I changed it. If you
# can accomplish the first rule with mocks then go ahead. I'm looking
# for the simplest/cleanest solution whatever that may be.
#
class Quiz
def initialize(input = STDIN, output = STDOUT)
@input = input
@output = output
end
def problem
first = rand(10)
second = rand(10)
@output.puts "What is #{first} + #{second}?"
answer = @input.gets
if answer.to_i == first + second
@output.puts "Correct!"
else
@output.puts "Incorrect!"
end
end
end
require "test/unit"
require 'stringio'
# This is a relatively general solution, based on "expect" type logic.
# The test object is a kind of a Fake, since it has semi-real state that persists
# throughout the test run. The callback expects a line and returns a line;
# that returned line is stashed away and used to answer the next 'gets' call.
# This logic could be generalized to an arbitrary series of scripted expectations
# and responses (though it'd be a bit more work to make a data structure that would
# capture that, other than the current mere '@response').
#
# I should emphasize that this is really an *integration* test, not a unit test.
# A unit test would be able to call methods and assert their return values.
# If I were # writing a unit test I'd refactor the hell out of the class under test
# so I could test/override/mock methods like the "rand" generator.
#
class QuizTest < Test::Unit::TestCase
class SmartIO < StringIO
def initialize(&callback)
super("")
@callback = callback
@step = 0
end
def puts(line)
@response = @callback.call(@step, line).to_s
@step += 1
end
def gets
@response || raise("gets called without a canned response")
end
end
def read_question(line)
line =~ /What is (\d+) \+ (\d)\?/
[$1.to_i, $2.to_i]
end
def test_correct
io = SmartIO.new do |step, line|
case step
when 0:
x,y = read_question(line)
(x+y)
when 1:
assert_equal "Correct!", line
nil
end
end
quiz = Quiz.new(io, io)
quiz.problem
end
def test_incorrect
io = SmartIO.new do |step, line|
case step
when 0:
x,y = read_question(line)
(x+y-1)
when 1:
assert_equal "Incorrect!", line
nil
end
end
quiz = Quiz.new(io, io)
quiz.problem
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment