Skip to content

wordy: Generate tests #509

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions exercises/wordy/.meta/.version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
4 changes: 4 additions & 0 deletions exercises/wordy/example.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
module BookKeeping
VERSION = 1
end

class WordProblem
attr_reader :question
def initialize(question)
Expand Down
17 changes: 17 additions & 0 deletions exercises/wordy/example.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require 'minitest/autorun'
require_relative 'wordy'

# Test data version: <%= sha1 %>
class WordyTest < Minitest::Test<% test_cases.each do |test_case| %>
def <%= test_case.test_name %>
<%= test_case.skipped %>
<%= test_case.workload %>
end
<% end %>

<%= IO.read(XRUBY_LIB + '/bookkeeping.md') %>
def test_bookkeeping
skip
assert_equal <%= version.next %>, BookKeeping::VERSION
end
end
101 changes: 66 additions & 35 deletions exercises/wordy/wordy_test.rb
Original file line number Diff line number Diff line change
@@ -1,97 +1,128 @@
#!/usr/bin/env ruby
gem 'minitest', '>= 5.0.0'
require 'minitest/autorun'
require_relative 'wordy'

class WordProblemTest < Minitest::Test
def test_add_1
assert_equal 2, WordProblem.new('What is 1 plus 1?').answer
# Test data version: aa12f2e
class WordyTest < Minitest::Test
def test_addition
# skip
question = 'What is 1 plus 1?'
assert_equal(2, WordProblem.new(question).answer)
end

def test_add_2
def test_more_addition
skip
assert_equal 55, WordProblem.new('What is 53 plus 2?').answer
question = 'What is 53 plus 2?'
assert_equal(55, WordProblem.new(question).answer)
end

def test_add_negative_numbers
def test_addition_with_negative_numbers
skip
assert_equal(-11, WordProblem.new('What is -1 plus -10?').answer)
question = 'What is -1 plus -10?'
assert_equal(-11, WordProblem.new(question).answer)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this one have brackets around the arguments?

Copy link
Contributor Author

@tommyschaefer tommyschaefer Dec 20, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi there!

I originally thought that it was something enforced by rubocop, but I'm not sure now. Whenever I save the file without the parentheses, I get a warning in Vim ambiguous first argument; put parentheses or a space even after '-' operator (only for cases with a negative number).

I'm assuming that's maybe why the original cases also had parens around negative cases?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's a Rubocop warning, it's not particularly important, but it's better to make all the cases consistent so in this case I'd wrap them all in parentheses. This has the pleasant side effect of making your value_assertion method much simpler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes. That sounds great to me!

end

def test_add_more_digits
def test_large_addition
skip
assert_equal 45_801, WordProblem.new('What is 123 plus 45678?').answer
question = 'What is 123 plus 45678?'
assert_equal(45801, WordProblem.new(question).answer)
end

def test_subtract
def test_subtraction
skip
assert_equal 16, WordProblem.new('What is 4 minus -12?').answer
question = 'What is 4 minus -12?'
assert_equal(16, WordProblem.new(question).answer)
end

def test_multiply
def test_multiplication
skip
assert_equal(-75, WordProblem.new('What is -3 multiplied by 25?').answer)
question = 'What is -3 multiplied by 25?'
assert_equal(-75, WordProblem.new(question).answer)
end

def test_divide
def test_division
skip
assert_equal(-11, WordProblem.new('What is 33 divided by -3?').answer)
question = 'What is 33 divided by -3?'
assert_equal(-11, WordProblem.new(question).answer)
end

def test_add_twice
def test_multiple_additions
skip
question = 'What is 1 plus 1 plus 1?'
assert_equal 3, WordProblem.new(question).answer
assert_equal(3, WordProblem.new(question).answer)
end

def test_add_then_subtract
def test_addition_and_subtraction
skip
question = 'What is 1 plus 5 minus -2?'
assert_equal 8, WordProblem.new(question).answer
assert_equal(8, WordProblem.new(question).answer)
end

def test_subtract_twice
def test_multiple_subtraction
skip
question = 'What is 20 minus 4 minus 13?'
assert_equal 3, WordProblem.new(question).answer
assert_equal(3, WordProblem.new(question).answer)
end

def test_subtract_then_add
def test_subtraction_then_addition
skip
question = 'What is 17 minus 6 plus 3?'
assert_equal 14, WordProblem.new(question).answer
assert_equal(14, WordProblem.new(question).answer)
end

def test_multiply_twice
def test_multiple_multiplication
skip
question = 'What is 2 multiplied by -2 multiplied by 3?'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the custom message that isn't currently generated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you work out how to make the generator add this message yourself, or would you like some hints?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hints would be perfect, actually 😄 ! I can't quite work out how I would implement this without modifying the shared test data

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my latest comments.

assert_equal(-12, WordProblem.new(question).answer)
end

def test_add_then_multiply
def test_addition_and_multiplication
skip
question = 'What is -3 plus 7 multiplied by -2?'
message = 'You should ignore order of precedence. -3 + 7 * -2 = -8, not -17'
assert_equal(-8, WordProblem.new(question).answer, message)
answer = WordProblem.new(question).answer
message = "You should ignore order of precedence. -3 + 7 * -2 = -8, not #{answer}"
assert_equal(-8, answer, message)
end

def test_divide_twice
def test_multiple_division
skip
question = 'What is -12 divided by 2 divided by -3?'
assert_equal 2, WordProblem.new(question).answer
assert_equal(2, WordProblem.new(question).answer)
end

def test_too_advanced
def test_unknown_operation
skip
question = 'What is 52 cubed?'
assert_raises ArgumentError do
WordProblem.new('What is 53 cubed?').answer
WordProblem.new(question).answer
end
end

def test_irrelevant
def test_non_math_question
skip
question = 'Who is the President of the United States?'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This question doesn't start with 'What is' but that's probably an issue for the common metadata.

assert_raises ArgumentError do
WordProblem.new('Who is the president of the United States?').answer
WordProblem.new(question).answer
end
end

# Problems in exercism evolve over time, as we find better ways to ask
# questions.
# The version number refers to the version of the problem you solved,
# not your solution.
#
# Define a constant named VERSION inside of the top level BookKeeping
# module, which may be placed near the end of your file.
#
# In your file, it will look like this:
#
# module BookKeeping
# VERSION = 1 # Where the version number matches the one in the test.
# end
#
# If you are curious, read more about constants on RubyDoc:
# http://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/constants.html
def test_bookkeeping
skip
assert_equal 1, BookKeeping::VERSION
end
end
67 changes: 67 additions & 0 deletions lib/wordy_cases.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
class WordyCase < OpenStruct
def test_name
'test_%s' % description.downcase.tr(' ', '_')
end

def workload
[
"question = '#{input}'",
indent(4, assertion),
].join("\n")
end

def skipped
index.zero? ? '# skip' : 'skip'
end

private

def indent(size, lines)
lines.lines.each_with_object('') { |line, obj| obj << ' ' * size + line }
end

def assertion
return error_assertion unless expected
return message_assertion if message

"assert_equal(#{expected}, WordProblem.new(question).answer)"
Copy link
Contributor

@Insti Insti Dec 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then you can add your custom failure message here.

message = failure_message || "Expected #{expected} but got #{result}"
"assert_equal(#{expected}, WordProblem.new(question).answer, #{message})"

(This code will not work as-is)

end

def error_assertion
[
'assert_raises ArgumentError do',
indent(2, 'WordProblem.new(question).answer'),
'end',
].join("\n")
end

def message_assertion
[
'answer = WordProblem.new(question).answer',
"message = \"#{message % '#{answer}'}\"",
"assert_equal(#{expected}, answer, message)",
].join("\n")
end
end

class WordyCase::PreProcessor
class << self
def call(row)
row.merge('message' => message_for(row))
end

private

def message_for(row)
return unless row['input'] == 'What is -3 plus 7 multiplied by -2?'

'You should ignore order of precedence. -3 + 7 * -2 = -8, not %s'
end
end
end

WordyCases = proc do |data|
JSON.parse(data)['cases'].map.with_index do |row, i|
WordyCase.new(WordyCase::PreProcessor.call(row).merge(index: i))
end
end
102 changes: 102 additions & 0 deletions test/wordy_cases_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
require_relative 'test_helper'

class WordyCaseTest < Minitest::Test
def test_test_name
test_case = WordyCase.new(description: 'description')

assert_equal 'test_description', test_case.test_name
end

def test_test_name_with_description_with_spaces
test_case = WordyCase.new(description: 'description with spaces')

assert_equal 'test_description_with_spaces', test_case.test_name
end

def test_skipped_with_zero_index
test_case = WordyCase.new(index: 0)

assert_equal '# skip', test_case.skipped
end

def test_skipped_with_non_zero_index
test_case = WordyCase.new(index: 1)

assert_equal 'skip', test_case.skipped
end

def test_workload_with_expected_and_no_message
test_case = WordyCase.new(expected: 1, input: 1)

expected_workload = [
'question = \'1\'',
' assert_equal(1, WordProblem.new(question).answer)',
].join("\n")

assert_equal expected_workload, test_case.workload
end

def test_workload_with_expected_and_message
test_case = WordyCase.new(expected: 1, input: 1, message: 'test %s')

expected_workload = [
'question = \'1\'',
' answer = WordProblem.new(question).answer',
' message = "test #{answer}"',
' assert_equal(1, answer, message)',
].join("\n")

assert_equal expected_workload, test_case.workload
end

def test_workload_without_expected
test_case = WordyCase.new(input: 1)

expected_workload = [
'question = \'1\'',
' assert_raises ArgumentError do',
' WordProblem.new(question).answer',
' end',
].join("\n")

assert_equal expected_workload, test_case.workload
end
end

class WordyCasePrProcessorTest < Minitest::Test
def test_call_as_non_special_case
row = { 'input' => '' }
processed_row = WordyCase::PreProcessor.call(row)

assert_equal({ 'input' => '', 'message' => nil }, processed_row)
end

def test_call_as_special_case
row = { 'input' => 'What is -3 plus 7 multiplied by -2?' }
processed_row = WordyCase::PreProcessor.call(row)
expected_row = {
'input' => 'What is -3 plus 7 multiplied by -2?',
'message' => 'You should ignore order of precedence. -3 + 7 * -2 = -8, not %s',
}

assert_equal expected_row, processed_row
end
end

class WordyCasesTest < Minitest::Test
def test_call
json = {
cases: [
{ description: 'test 1' },
{ description: 'test 2' },
]
}.to_json

expected_cases = [
WordyCase.new(description: 'test 1', message: nil, index: 0),
WordyCase.new(description: 'test 2', message: nil, index: 1),
]

assert_equal expected_cases, WordyCases.call(json)
end
end