-
-
Save alexch/112466 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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