Crafting happiness with Free Software & Hardware

« Test Driven Development by Example » – Chapter 20. Cleaning up After

Guile Logo

Previously :

The next item on the todo list is Invoke tearDown afterward. But before doing this one, Kent wanted to take care of the order in which the procedures are called. This brings a new item : Log strings in WasRun.

So in this chapter we are going to implement a small logging system then the tearDown test fixture.

Kent started by adding log field in the WasRun's setUp method. This allows him to change testSetUp() to look at the log instead of the flag. Finally, he deleted the wasSetUp flag.
Once it's done. He did the same with the was-run flag. Finally, no need for two different tests as one single assertion check if the setup and the test method are called (and the call order).

class TestCase:
    def __init__(self, name):
        self.name = name
    def setUp(self):
        pass
    def run(self):
        self.setUp()
        method = getattr(self, self.name)
        method()

class WasRun(TestCase):
    def __init__(self, name):
        TestCase.__init__(self, name)
    def setUp(self):
        self.log = "setUp "
    def testMethod(self):
        self.log = self.log + "testMethod "

class TestCaseTest(TestCase):
    def testTemplateMethod(self):
        test = WasRun("testMethod")
        test.run()
        assert("setUp testMethod " == test.log)
    TestCaseTest("testTemplateMethod").run()

Here is mine:

(define-module (xunit-tdd test-case)
  #:use-module (srfi srfi-9)
  #:export (log set-log!))

(define-record-type <test-case>
  (make-test-case setup-proc test-proc log)
  test-case?
  (setup-proc setup-proc)
  (test-proc test-proc)
  (log log set-log!))

(define-public new
  (case-lambda
    ((a-test-proc)
     (make-test-case (const #f) a-test-proc #f))
    ((a-setup-proc a-test-proc)
     (make-test-case a-setup-proc a-test-proc #f))))

(define-public run
  (lambda (test-case)
    ((setup-proc test-case) test-case)
    ((test-proc test-case) test-case)))

(define-module (xunit-tdd was-run)
  #:use-module ((xunit-tdd test-case) #:prefix test-case:))

(define-public new
  (lambda ()
    (test-case:new test-setup test-procedure)))

(define test-setup
  (lambda (test-case)
    (test-case:set-log! test-case "test-setup ")))

(define test-procedure
  (lambda (test-case)
    (test-case:set-log! test-case (string-append (test-case:log test-case) "test-procedure "))))

(define-module (tests test-case-test)
  #:use-module ((rnrs) #:version (6) #:select (assert))
  #:use-module ((xunit-tdd test-case) #:prefix test-case:)
  #:use-module ((xunit-tdd was-run) #:prefix was-run:))

(define test-template-method
  (lambda (this-test-case)
    (let ([a-test-case (was-run:new)])
      (test-case:run a-test-case)
      (assert (string=? "test-setup test-procedure " (test-case:log a-test-case))))))

(test-case:run (test-case:new test-template-method))

The next step is to handle the tearDown feature ! Pretty simple. Still, writing the test first. Et voilà !

Kent's Python version:

class TestCase:
    def __init__(self, name):
        self.name = name
    def setUp(self):
        pass
    def tearDown(self):
        pass
    def run(self):
        self.setUp()
        method = getattr(self, self.name)
        method()
        self.tearDown()

class WasRun(TestCase):
    def __init__(self, name):
        TestCase.__init__(self, name)
    def setUp(self):
        self.log = "setUp "
    def testMethod(self):
        self.log = self.log + "testMethod "
    def tearDown(self):
        self.log = self.log + "tearDown "

class TestCaseTest(TestCase):
    def testTemplateMethod(self):
        test = WasRun("testMethod")
        test.run()
        assert("setUp testMethod tearDown " == test.log)
    TestCaseTest("testTemplateMethod").run()

My Guile version:

(define-module (xunit-tdd test-case)
  #:use-module (srfi srfi-9))

(define-record-type <test-case>
  (make-test-case setup-proc test-proc teardown-proc log)
  test-case?
  (setup-proc setup-proc)
  (test-proc test-proc)
  (teardown-proc teardown-proc)
  (log log set-log!))

(define-public (new a-setup-proc a-test-proc a-teardown-proc a-log)
  (make-test-case a-setup-proc a-test-proc a-teardown-proc a-log))

(define-public (run test-case)
  (for-each
   (lambda (proc)
     (if (procedure? (proc test-case))
         ((proc test-case) test-case)))
   (list setup-proc test-proc teardown-proc)))

(define-public (read-log test-case)
  (log test-case))

(define-public (append-to-log! test-case a-log)
  (set-log! test-case (string-append (log test-case) a-log)))


(define-module (xunit-tdd was-run)
  #:use-module ((xunit-tdd test-case) #:prefix test-case:))

(define-public (new)
  (test-case:new test-setup test-procedure test-teardown ""))

(define (test-setup test-case)
  (test-case:append-to-log! test-case "test-setup "))

(define (test-procedure test-case)
  (test-case:append-to-log! test-case "test-procedure "))

(define (test-teardown test-case)
  (test-case:append-to-log! test-case "test-teardown "))


(define-module (tests test-case-test)
  #:use-module ((rnrs) #:version (6) #:select (assert))
  #:use-module ((xunit-tdd test-case) #:prefix test-case:)
  #:use-module ((xunit-tdd was-run) #:prefix was-run:))

(define (test-template-method this-test-case)
  (let ([a-test-case (was-run:new)])
    (test-case:run a-test-case)
    (assert (string=? "test-setup test-procedure test-teardown "
		      (test-case:read-log a-test-case)))))

(test-case:run (test-case:new #f test-template-method #f ""))

The more I go through the exercise, the more I see how Kent's version is more compact. Even if I were using some syntax sugar, I can see OO benefits right here.
The more I work on this, the more I want to dig in to the functional spirit. A step toward this direction would be using immutable reccords… Will see if I find an idea to do it!

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 ! Either via RSS or via e-mail subscription !

And more importantly, share this blog and tell your friends it's the best blog in the history of Free Software! No kidding!

#gnu #guile #tdd #book #english