R
Robert Klemme
I usually find that it really doesn't matter how fast things run if
they produce the wrong results.
Well, if it takes ages until you find out because you have to wait for
results...
SCNR
robert
I usually find that it really doesn't matter how fast things run if
they produce the wrong results.
I interpreted the two types of testing as follows.
Assume you have a method that expects a single non-negative integer
argument. You'll probably want to test that it behaves correctly
for the following:
foo(0) # edge case
foo(1) # typical call
foo(36) # random case from all values
foo(100000000) # large magnitude
foo(1023) # testing near powers of two
foo(1024)
foo(1025)
These tests will be designed to make sure that foo works correctly
on *expected* input.
Then there is the question about whether you should test:
foo(-1)
foo(nil)
foo("bogus")
foo("too", "many", :arguments)
foo()
and so on. These tests are throwing *unexpected* input at the
method. Bertrand Meyer in his writings about design by contract
programming would say that foo isn't obligated to do anything at
all when called with arguments that are outside its 'contract'.
If foo blows up on that input it isn't the failure of 'foo' but
instead is the failure of the caller to adhere to the contract.
So the philosophical question is should you extend foo's contract
to say that it will raise a particular exception (ArgumentError),
add the code to foo, and keep the tests or instead should you
decide that the tests are superfluous and instead focus on
better tests for the code that eventually calls foo (i.e. make
sure your code never calls foo incorrectly).
OTOH if they run forever they cannot produce wrong results...Well, if it takes ages until you find out because you have to wait for
results...![]()
"Vive la France".shall.translate.to "Viva Italia"
"Je ne suis pas disponible".shall.translate.to "Gone fishing"
Now that is normally considered enough. But for my personal comfort
and anticipating problems and bugs inside my great plan of
implementation - experience speaking. I will add some tests very
closely related to the implementation. You see, something totally
opposed to the BDD thing.
E.g.
assert_kind_of? Enumeration, my_thingy.instance_variable_get("@words")
but better would be duck type tests
assert_respond_to? :each, my_thing.....
assert [:each, :map, :size] <= my_thingy.ivarget("@words").methods
Ah! You're talking about stories (integration tests in TDD), which specify
how the *system* behaves in terms of the *user*'s exeperience, vs.
specifications (unit tests in TDD), which specify how an *object* behaves
in terms of *another object*'s experience.[1]
The current RSpec trunk is actually taking this conversation to a really
cool place; the acceptance/integration tests are called "stories", and are
written in plain text, ideally even by the customer/client instead of the
developer, e.g.:
Story: translate languages while confusing nationalism
As an English speaker
I want to translate foreign languages into English
And mix up foreign countries
So that I can read it without needing maps, because
US Americans do not have maps, such as, the Iraq.
Scenario: General French text
Given the original text is "Je ne suis pas disponible"
And the original language is French
When I press the translate button
Then the new text should be "Gone fishing"
Scenario: French nationalism/country mention
Given the original text is "Vive la France"
And the original language is French
When I press the translate button
Then the new text should be "Vive Italia"
That's it. That's the exact format of a story. You'd then have code that
deals with the parameters in each step:
steps = StepGroup.new do |define|
define.given("the original text is \"$text\"") do |text|
@translator.set_text text
...etc...
end
end
On the other hand, your specs (unit tests in TDD) are written more
programmatically; the intended reader/writer is a developer, and it saves
having to write step groups:
# Parser#parse will return either a Nationalism or a Translatable object.
p = Parser.newfrench)
p.parse("Bon soir").should_be kind_of?(Translatable)
p.parse("Vive la France").should_be kind_of?(Nationalism)
lambda { p.parse("Hello, I must be going) }.should_raise
Exception::AlreadyEnglish
etc.
For more on plain-text stories, check out:
http://blog.davidchelimsky.net/articles/2007/10/21/story-runner-in-plain-english
or
http://rubyurl.com/2AS
[1] Stolen shamelessly from a recent David Chelimsky post to the
rspec-users list
--
Jay Levitt |
Boston, MA | My character doesn't like it when they
Faster: jay at jay dot fm | cry or shout or hit.
http://www.jay.fm | - Kristoffer
Want to reply to this thread or ask your own question?
You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.