DRY test generators#566
Conversation
converted: - hamming - ocr-numbers - luhn - pig-latin to use it
|
|
||
| class ExerciseCase < OpenStruct | ||
| def name | ||
| 'test_%s' % description.gsub(/[^\w ?!]/, '').gsub(/[- ]/, '_') |
There was a problem hiding this comment.
Does this change cause the test names to change by not presenting hyphenated words as individual words, but rather as shown in this diff:
- def test_large_distance_in_off_by_one_strand
+ def test_large_distance_in_offbyone_strand
I would like to place the change back in, due to things like "non_unique" being acceptable and readable, even if it technically isn't a separate word, and to avoid "off-by-one" becoming "offbyone".
There was a problem hiding this comment.
Will do (in the morning!)
There was a problem hiding this comment.
I think it is a separate commit, so it may be a simple process. That was a great idea to separate it out like that!
There was a problem hiding this comment.
I'm not sure if this is premature:
We will need a underscore_format(string)* method that we can have a bunch of unit tests for so we can keep track of all the weird cases we encounter.
So this line becomes:
'test_' + underscore_format(description)
* name and whether it should be a String refinement TBD.
|
The design principles are very much in line with what we want to do, I think.
These places would definitely benefit from being named. |
|
Firstly: Awesome PR description ❤️ thanks.
This is not a requirement, feel free to break as much stuff as you like.
Excellent.
Excellent.
Excellent. |
| @@ -1,2 +1,30 @@ | |||
| require 'ostruct' | |||
There was a problem hiding this comment.
This file should be in `lib/generator'
There was a problem hiding this comment.
Indeed! I'll go ahead and move it (also in the morning).
| require 'ostruct' | ||
| require 'json' | ||
|
|
||
| class ExerciseCase < OpenStruct |
There was a problem hiding this comment.
Not an issue for this PR, but ExerciseCase should not be an OpenStruct and the canonical data should be kept separate from the methods of this class.
See: my imagined version of ExerciseCase for an example.
|
|
||
| def assertion | ||
| expected ? 'assert' : 'refute' | ||
| %Q(#{assert_or_refute} Luhn.valid?(#{input.inspect})) |
There was a problem hiding this comment.
I like that all these need to do now is implement workload.
|
All these TODOs probably deserve their own issue. TODO
Yes.
Yes. Now some of the things from the grand design in my head: Note: "problem" in all these examples is the variable exercise name.
Some of this will require changes to the bin/generator code. By creating the new Everything needed to create a generator for an exercise will be in |
|
For reference, here is my proof of concept for this from a few months ago: Insti#1 |
| expected.to_i == -1 | ||
| end | ||
|
|
||
| def assert_or_refute |
There was a problem hiding this comment.
Can this be just assert it doesn't REALLY matter if the final test file contains refute and
"#{assert} Problem.call(#{input.inspect})"
looks better in the readme than:
"#{assert_or_refute} Problem.call(#{input.inspect})"
There was a problem hiding this comment.
Future:
It may be possible to add logic into the assert method that works out the correct assert to use including assert_equal or assert_error
|
I think that's all for now. |
tommyschaefer
left a comment
There was a problem hiding this comment.
This looks awesome!! Thank you so much for your work on this!
| end | ||
|
|
||
| def test_unreadable_but_correctly_sized_inputs_return_? | ||
| def test_Unreadable_but_correctly_sized_inputs_return_? |
There was a problem hiding this comment.
I think ExerciseCase#name should probably also downcase the description.
There was a problem hiding this comment.
What to do about punctuation characters is another issue.
But this one is probably a job for a pre-processor, since it's problem dependent.
s/"?"/question mark string/ as the json description is being read in and before ExerciseCase#name sees it.
There was a problem hiding this comment.
Heh. I had downcase in there at one point, then took it out. Shoulda trusted my first instincts on that one 😄
| @@ -1,5 +1,3 @@ | |||
| require 'exercise_cases' | |||
There was a problem hiding this comment.
Not entirely sure I agree with removing this line.
Sure require_all is awesome, but there's only one dependency and it's nice to be able to see where ExerciseCase comes from.
There was a problem hiding this comment.
And it simply doesn't break anything... if that dependency goes away, no problem, it is a documentation value at this point.
| assert_equal 'A string with TLA'.underscore, 'a_string_with_tla' | ||
| end | ||
|
|
||
| def hyphenated_text |
| class LuhnCase < ExerciseCase | ||
| def workload | ||
| %Q(#{assert_or_refute} Luhn.valid?(#{input.inspect})) | ||
| "#{assert} Luhn.valid?(#{input.inspect})" |
There was a problem hiding this comment.
It's a good idea to keep your commits atomic.
This has nothing to do with underscores and should be in its own commit.
It is a discipline that needs to be trained. Commit early, commit often.
| @@ -1,5 +1,3 @@ | |||
| require 'exercise_cases' | |||
There was a problem hiding this comment.
Should have been another separate commit.
| assert_raises(ArgumentError) { <%= test_case.workload %> }<% else %> | ||
| assert_equal <%= test_case.expected %>, <%= test_case.workload %><% end %> | ||
| <%= test_case.skipped %> | ||
| <%= test_case.workload %> |
| else | ||
| "assert_equal #{expected.inspect}, #{test_case}" | ||
| assert_equal { test_case } | ||
| end |
There was a problem hiding this comment.
Definitely a better place for this.
|
Putting this here so we don't forget: We currently create |
Motivation and Context
The test generators have a lot of duplicate code. Do we need to discuss why that's not good? I decided to take a pass at DRYing up the test generators before moving on to writing a test generator scaffold generator...
Design principles:
This effort:
Description
I introduced a base class,
ExerciseCaseintolib/exercise_cases.rbfrom whichProblemNameCasecan inherit.ExerciseCaseprovides public methodsnameandskippedas well as a variety of protected helper methods.I converted three generators I wrote, and one I didn't write, to use
ExerciseCase. The generated test files remain (almost) the same while the generators exhibit far more consistency. (The only changes to the test files are some small changes in how the descriptions are processed to produce the test names.I updated the README to reflect the changed workflow.
How Has This Been Tested?
I can regenerate the tests with no changes except for the small changes noted above.
The tests pass under
rake test.Next Steps
ProblemNameCaseslogic. We can inspect and extract the cases from the JSON programmatically.