Test Driven Development (TDD) and APL April 29, 2012

As an Agilist and XP practitioner, it is not only about the values and how you are as a human towards others. That is the easy part (yes it is! Even if you are a stereotypical nerd), the hard part is getting used to the discipline required to perform the practices.

Luckily my favourite practice is TDD, and my intent is to do TDD while developing APL programs! For this journey, I intend to develop a good-enough Booking backend system with APL and TDD.

Of we go and two different files are created, one Unit test (UT) file booking_unittests.dyalog and one implementation file booking.dyalog. In the UT file, I put the tests into different procedural functions, naming them UT1 , … , UTX.

The first tests is the lookup of a Bookable Unit’s name by it’s ID.

For this, I need some kind of infrastructure, a simple mechanism that will execute my tests and report the ones that fail. I want the Unit Test framework to be lightweight and allow me to keep my freedom. As for all functional programmers out there – the natural thing to do is to write your own.

The framework consists of two procedural functions, RunTest and Test. I define each test as a procedural function as well, and “register” the test for running inside Test

∇ Z ← RunTest Name Z ← ⍎ Name :If ~ Z ⎕ ← '-------------------------' ⎕ ← 'Unit test ' , Name , ' failed' ⎕ ← ⎕vr Name :Endif ∇ ∇ Z ← Test; Tests Tests ← 'UT1' Z ← RunTest Tests :if ^ / Z ⎕ ← 'All ' (⍴ Tests ) 'tests passed' :endif ∇

This example shows that ‘UT1’ is to be executed when I run all the Unit Tests.

So, my first test is to check whether I can get the Name by the ID.

As usual, I first run the tests, making sure it will fail

Booking_UT.Test ------------------------- Unit test UT1 failed ∇ Z ← UT1 [ 1 ] Z ← 'a' ≡ #.Booking.GETName UTNameTable 1 ∇

Seems, to work as I expected. So I write the implementation in booking.dyalog and add another test and implementation for the lookup based on ID to Name.

: Namespace Booking ∇ Z ← GETName Input; LookupTable ; BID ( LookupTable BID ) ← Input Z ← LookupTable [ LookupTable [ ; 1 ] ⍳ BID; 2 ] ∇ ∇ Z ← GETID Input; LookupTable ; Name ( LookupTable Name ) ← Input Z ← LookupTable [ LookupTable [ ; 2 ] ⍳ Name; 1 ] ∇ : EndNamespace

and the two tests (with framework – I should also be able to split the framework into its own module)

: Namespace Booking_UT UTNameTable ← ↑ ( 1 'a' ) ( 2 'b' ) ( 3 'c' ) ∇ Z ← UT1 Z ← 'a' ≡ #.Booking.GETName UTNameTable 1 ∇ ∇ Z ← UT2 Z ← 3 ≡ #.Booking.GETID UTNameTable 'c' ∇ ∇ Z ← RunTest Name Z ← ⍎ Name :If ~ Z ⎕ ← '-------------------------' ⎕ ← 'Unit test ' , Name , ' failed' ⎕ ← ⎕vr Name :Endif ∇ ∇ Z ← Test; Tests Tests ← 'UT1' 'UT2' Z ← RunTest ¨ Tests :if ^ / Z ⎕ ← 'All ' (⍴ Tests ) 'tests passed' :endif ∇ : EndNamespace

run the test again for success

⎕ ← ⎕SE .SALT.Load '/home/gianfranco/APL/Projects/Booking/book*' #.Booking #.Booking_UT Booking_UT.Test All 2 tests passed

Now there is an obvious pattern in both lookup functions, and having regression tests already makes a refactoring attempt safe as the tests will guide me.

After refactoring and running the tests, the implementation looks a bit slimmer

: Namespace Booking GETName ← { Lookup ⍵[ 1 ], 1 ,⍵[ 2 ] } GETID ← { Lookup ⍵[ 1 ], 2 ,⍵[ 2 ] } ∇ Z ← Lookup Input; LookupTable ; Col ; Key ( LookupTable Col Key ) ← Input Z ← LookupTable [ LookupTable [ ; Col ] ⍳ Key; 1 + 2 | Col ] ∇ : EndNamespace

However, I have a mental thorn in my eye because of the recurring ⍵[1],1,⍵[2] and ⍵[2],2,⍵[2] pattern. This needs to be addressed. The most obvious solution is to throw the arguments around for the base Lookup procedural function. A quick change

: Namespace Booking GETName ← { Lookup ⍵, 1 } GETID ← { Lookup ⍵, 2 } ∇ Z ← Lookup Input; LookupTable ; Col ; Key ( LookupTable Key Col ) ← Input Z ← LookupTable [ LookupTable [ ; Col ] ⍳ Key; 1 + 2 | Col ] ∇ : EndNamespace

and test run, shows that this is a solid refactoring

⎕ ← ⎕SE .SALT.Load '/home/gianfranco/APL/Projects/Booking/book*' #.Booking #.Booking_UT Booking_UT.Test All 2 tests passed

Nice!

Now time for some more serious functions, I need something to actually perform a booking, given a TimeTable, an Id and Some Timeslots. As this is a blog post, I will not make it much longer than this last booking test, however, I intend to publish the code on github.

As usual, I use the Test to reason about and structure the function and the input data, my test thus looks like this

∇ Z ← UT3; BookingSlots ; BookingId ; Expected BookingSlots ← ( 1 2 3 ) ( 3 1 ) ( 2 1 3 ) BookingId ← 5 Expected ← ↑ ( 3 5 0 0 ) ( 1 0 5 5 ) ( 2 5 0 5 ) Z ← Expected ≡ #.Booking.Book UTTable BookingSlots BookingId ∇

and running all the tests show that it’s failing as expected (I just wrote an empty skeleton returning the input table)

⎕ ← ⎕SE .SALT.Load '/home/gianfranco/APL/Projects/Booking/book*' #.Booking #.Booking_UT Booking_UT.Test ------------------------- Unit test UT3 failed ∇ Z ← UT3; BookingSlots ; BookingId ; Expected [ 1 ] BookingSlots ←( 1 2 3 )( 3 1 )( 2 1 3 ) [ 2 ] BookingId ← 5 [ 3 ] Expected ←↑( 3 5 0 0 )( 1 0 5 5 )( 2 5 0 5 ) [ 4 ] Z ← Expected ≡ #.Booking.Book UTTable BookingSlots BookingId ∇ 1 1 0

Now, the implementation that passes the test is

∇ Z ← Book Input; Table ; Bookings ; BookingId ; Rows ; Slots ( Table Bookings BookingId ) ← Input Rows ← Table [ ; 1 ] ⍳ ⊃¨ Bookings Slots ← 1 ↓¨ Bookings { Table [ 1 ↑⍵ ; 1 + 1 ↓⍵] ← BookingId } ¨ Rows ,¨ Slots Z ← Table ∇

and the proof that it works is my test

⎕ ← ⎕SE .SALT.Load '/home/gianfranco/APL/Projects/Booking/book*' #.Booking #.Booking_UT Booking_UT.Test All 3 tests passed 1 1 1

I leave this here together with a link to the Git repo where you can see the project continue and take form.

Cheers

(really looking forward to all the comments)