« Test Driven Development by Example » – Chapter 18. First Steps to xUnit
This first step, to an xUnit implementation, was pretty fun. You can taste it with the introductory sentence :
« Driving a testing tool using the testing tool itself to run the tests may seem a bit like performing brain surgery on yourself. »
Let's see how far can I push this adaptation in the Guile programming language.
So this is a bootstrapping process. Kent moves meticulously. Baby steps are the way to go ! It seems to be helpful to him as the one who write code, and it is also useful to me as the one who read and learn. I really like to understand how more experienced developers think, how they make decisions.
The chapter ends with the following test :
class TestCaseTest(TestCase): def testRunning(self): test = WasRun("testMethod") assert(not test.wasRun) test.run() assert(test.wasRun) TestCaseTest("testRunning").run()
TestCaseTest, the class which contains the driving tests, inherits from the
class TestCase: def __init__(self, name): self.name = name def run(self): method = getattr(self, self.name) method()
TestCase class has another subclass which is
class WasRun(TestCase): def __init__(self, name): self.wasRun = None TestCase.__init__(self, name) def testMethod(self): self.wasRun = 1
You already can see a scaffolding of classes – the design – emerging. Thanks to inheritance, encapsulation… And of course some design principles (I refer to the SOLID principles).
In my experiment, I won't have those properties in my tool belt. At first, what I ended up writing felt to lack sophistication, as if I missed a refactoring. I only have one module containing a data structure and procedures to apply onto.
Then, I read a quote from Alan Perlis' Epigrams on Programming (1982) :
« It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures. »
It kind of helped me to balance my feelings and I was able to continue my adaptation.
Here is my test :
(define-module (tests case-test) #:use-module ((rnrs) #:version (6) #:select (assert)) #:use-module ((xunit-tdd test-case) #:prefix test-case:)) (define test-running (lambda (test-case) (let ([a-test-case (test-case:init test-case:test-procedure)]) (assert (not (test-case:was-run a-test-case))) (test-case:run a-test-case) (assert (test-case:was-run a-test-case))))) (let ([test-case-test (test-case:init test-running)]) (test-case:run test-case-test))
And here is the code under test :
(define-module (xunit-tdd test-case) #:use-module (srfi srfi-9) #:export (was-run)) (define-record-type <test-case> (make-test-case was-run proc-name) was-run? (was-run was-run set-was-run!) (proc-name proc-name)) (define-public init (lambda (proc-name) (make-test-case #f proc-name))) (define-public run (lambda (test-case) ((proc-name test-case) test-case))) (define-public test-procedure (lambda (test-case) (set-was-run! test-case #t)))
My implementation has its limit in terms of functional compliance : my
<test-case> data structure is mutable to hold the
was-run information. I wish I could have found something else.
Thank you very much for reading this article!
Don't hesitate to give me your opinion, suggest an idea for improvement, report an error, or ask a question ! I would be so glad to discuss about the topic covered here with you ! You can reach me here.
Don't miss out on the next ones !
And more importantly, share this blog and tell your friends it's the best blog in the history of Free Software! No kidding!