TDD, BDD, executable specification#
Thu Apr 7 14:26:57 2011
The new system at $WORK finally went live about a week ago, hurrah.
The upgrade itself took a few hours longer than I'd have liked, and
(short shameful confession time), some (but probably not all) of this
could have been caught by better test coverage. Which set me on a
path towards
SimpleCov
(I'm using Ruby 1.9, rcov doesn't work), which led me to start looking
at the uncovered parts, which set me to thinking. Which, as we all
know, is dangerous.
TDD advocates (and pro-testing people in general) say "Don't test
accessors". There are two reasons to say this that seem to my mind
like good reasons: Ron says
it
because he wants you to write tests that do something else (something
useful) that happens to involve calling those accessors.
J B Rainsberger
says it because "get/set methods just can't break, and if they can't
break, then why test them?"
The problem comes when you adopt the mindset typified by BDD that "the
test examples are actually your executable specification", because in that
case how do you specify that the object has an accessor? This is
not an unreasonable demand. Suppose we have objects whose purpose is
to store structured data that will be used by client code - for
example, User has an age property. Jbrains - which must surely be
the Best Nickname Ever for a Java guy - says there's no useful test
you can write for this (or not unless you don't trust your platform or
something, but that way madness lies). But even if we are going to
write one: a test that stores one or a few example values can easily
be faked by the bloody-minded implementor ("the setter is called with
the argument 42, then the getter is called and should return 42? I
know! def age; 42; end") and a test that stores all possible values
and tests they can be retrieved will take forever to write/read/run.
Really the best notation in which to specify the behaviour of that
property is the same notation which, when run by the interpreter,
will implement the said behavior -
attr_accessor :age
It's not just accessors either. Everything on the continuum between
declarations of constants (
SECONDS_PER_DAY=86400, are you
really
going to write a test for that?) and simple mathematical formulae
class Triangle
def area
self.base * self.height / 2.0
end
end
are most readably expressed to humans as, well, the continuous
functions that they are, not the three or four example data points
that we might write examples to test for. For any finite number of
test cases, you can write a giant
case...when statement that passes
all of them and still doesn't work in the general case.
Yes, we could and often should write a couple of tests just to make
sure we haven't done anything boneheaded in implementing the
function, but they're not spec. They're just examples.
But here's the rub: where or how do we put that code to make it
obvious that it's specification that happens also to be a valid
implementation - and not just implementation that may or may not meet
a spec expressed in some other place/form? If we're laying out our
app in conventional Ruby style, it can't go in the spec/ directory
because that doesn't actually get run as part of the application, and
it shouldn't go in lib/ or models/ or wherever else unless we are
prepared to make our clients rootle through all that code looking for
whatever "this is specification not just an implementation detail"
flag we decide to adopt) when they want to use our interface.
I'm going to make a suggestion which is either radical or bone-headed:
we should smush the rspec-stuff together with the app code: embed examples (which may in
some cases be specification and in other cases be "smoke tests")
in the same files as the implementation (which may itself
sometimes be specification and other times be the result of our
fallible human attempts to derive implementation from spec), and then
we can have some kind of annotations to say which is which, and then
we can have some kind of rdoc-on-'roids literate programming tool (To Be Implemented) go through the whole
lot and produce two separate documents. One for people who want to
use our code and want to know what it should do, and the other for
the people who have to hack on it and need to know how it does. Or
doesn't. And then maybe we can have code coverage metrics that
actually mean something.
What I miss most about Lisp#
Sat Apr 30 22:23:33 2011
It's been three months since I wrote anything longer than one line in
Lisp, and over a year since I wrote more than a screenful of the
stuff.
What I miss most is not CLOS or the REPL or even macros (per se,
anyway). It's
- the distinction between READ and EVAL: a sane syntax for
constructing complex data structures that look like code, but
without actually having the data structures in question interpreted
- and backtraces with the values of function parameters in them. When
you're doing the same thing 1000 times to different database rows or
objects in a collection, and one of them has a
nil in it
somewhere, it would be really nice to know which one.
And maybe the REPL (although irb does most of that). And kinda sorta
Defsystem, but I seem to be in the process of
reimplementing that ...
But I hope soon to get Rubinius installed, just because I still have
the irrational opinion that a grown-up programming language ought to
be able to implement itself (and I have a thing for native code) so
project 1 there is to see if I can hack the backtrace thingy at least
into it.
Introducing Projectr#
Mon May 2 09:39:18 2011
Why might you want to know the names of all the files in your project?
One might turn the question around and ask why would you possibly
would not want to, but maybe that's not a constructive dialogue. So
let's list some use cases
- to load them into your application
- to load them into irb for debugging or for help in constructing test cases
- to process them through rdoc
- to put them in a gem
- to print them (don't laugh, I did that the other day when I was having trouble deciding how to refactor a bunch of stuff)
As far as I can see from my survey of the Ruby world, the current
practices for each of these use cases are pretty ad hoc. Maybe you
write a file full of require or require_relative statements (as
the RBP
blog
author likes to do), maybe you use a glob, maybe you write a MANIFEST
file, but there seems to be a significant lack of DRYness about it
all. This led me to think there is a gap in the market for
- a language for describing the files that a project comprises
- some tools to interrogate a project description written in this form
and find out what's in it
- some code to load them into a running interpreter - and for bonus
points, when files have previously been loaded into said image but
since changed on disk, to reload them. This could be used in irb
sessions, or could form the basis of a development-oriented web
server that reloads changed files without needing to be stopped and
started all the time
Note that item 3 above gives us something that "file containing list
of require statements" doesn't, because it allows us to reload
files that we've already seen instead of just saying "meh, seen it
already". If you're using a comparatively low-powered machine then
reloading your entire app in irb every time you change a method
definition is unnecessarily and obviously slow. If you're also using
Bundler (which I rather like now i's settled down a bit, and will
write more about in a future entry) then the additional bundle exec
is not just slow, it's SLow with a capital S and a capital L and a
pulsating ever-growing O that rules from the centre of the underworld.
Here's one I made earlier
Projectr::Project.new :test do
# directories may be named by symbols or strings
directory :example do
#as may files
file "file1"
file :file2
directory "subdir" do
file :subdir_file
end
end
end
h=Projectr::Project[:test]
h.load! # it loads all the files
- and again
h.load! # nothing happens this time
- touch example/file1.rb
h.load! # loads only the changed file
At the time of writing this, the
github version does about that much, but
is quite clearly still version 0. Stuff I am still thinking about:
- Load-order dependencies. Lisp programmers may recognise that
Projectr was inspired by using (and indeed implementing a version
of) defsystem
(or more recently
here
but Projectr is almost minimally featured compared to any of the
Lisp-based defsystem facilities. Many of those features I don't
have any strong evidence that the Ruby world would find use for, but
load-order dependencies allow us to say for example that if file A
defines a DSL and files B and C use that DSL, changing A should make
the computer reload B and C as well
- It seems clear to me that defining a project and loading it are two
separate operations - you may wish instead to define it and then
generate a Gemspec, for example - but there's still a lot of verbiage in
the common case that you do want to load it, and I haven't really found
file layout and naming conventions that I feel good about
- likewise, what happens when we redefine the project itself (as would
happen if we want to add a file to it, for example) is slightly up
for grabs. Should the project definition file be considered a part
of the project?
I will doubtless form my own opinions on all of these issues in time
and with more experience of using this tool in practice, but feedback
on them and on the general approach is warmly welcomed.
Fork, clone, spindle, mutilate