« This weblog is a successor of Onegasoft site | Main | “Constant” custom functions to save time and write clearer code »

Develop custom functions more effectively using a simple test unit

An effective approach to writing code is to use automated tests. It's very simple to use the method when developing FileMaker custom functions. All it takes is a little table (you can use the provided sample) and about five minutes to add it to the project you develop.

Technorati tags: , , , , .

Using automated tests is often associated with extreme programming methodology. Short, the method is the following:
  • There must be a tool to run tests automatically.
  • Tests must be written before the code they are supposed to test.
  • The code is being written until all tests pass.

Let me show you how simple it is to make such an automated tester for FileMaker custom functions and then I'll amplify why the methodology turns out to be really effective. As I said, I already have a sample tester, but if this approach is new to you, you'd better follow the process step by step.

The simplest possible tester

You need a single test table; I suggest putting it in a separate file that can be reused among all the applications you develop. The table requires the following fields:Test, Result; both are text. The Test stores a FileMaker formula that evaluates to an empty value, if the test pass, and to a non-empty value, if it doesn't.

Assume you want to write a function Years( date 1, date 2 ) that must return a number of years between two dates. How you're going to test it works?

Of course, it must really provide the basic functionality: given a date, say, year an a half ago, it must return 1; given a date a couple of months ago it must return 0. Since you probably won't write a special handler for every possible date, it's sufficient to test only “frontiers”. Type the following text in the Test field:

Let( [
today =
Get( CurrentDate ),
one year ago =
Date( Month( today ), Day( today ), Year( today ) - 1 );
almost one year ago =
one year ago + 1 ];

Case( Years( one year ago, today ) ≠ 1, "Error" ) &
Case( Years( almost one year ago, today ) ≠ 0, "Error" ) )

Now make a button to set the Result field to

Evaluate( Test::Test )

Now with just a single click of a button you can run this simple test for your function: if the result is empty, the function works, if not, something is wrong and you should check the function code.

So why it's effective?

  • Automated tests require very little additional effort to be run. Here you have to just press a button to run a test or all tests at once. This means you can test the code at almost every corner and always know whether it's solid or weak. Basically as a new custom function is added or another one is changed, you can run all the tests again to see whether or not the new code has integrated seamlessly.
  • If you write tests before code, they serve as working specifications, and such specifications by their nature are more correct, elaborate and up-to-date than, say, paper ones. For an example of such a specification see the description of the actual test of the Years() function below.
  • As soon as you have a test you're to pass, it works as a clear goal to you, so you're less likely to waste time inventing code more complex than it needs to be.
  • You can easily share the file among different FileMaker applications and though you cannot copy functions yet, you can reuse tests for them.

The sample tester

The actual tester isn't that geeky as it may seem from the concise description above. It has additional “name” field, form, list and help screens, a few helper buttons and a couple of icons to better indicate results.

List.png Form.png

I also prefer to write tests using a special test function Assert Equals( actual value, expected value ) (described elsewhere) that makes the test specification clearer. (Note that this function has to be copied into the file you're testing.) For example, the actual test for the Years() function was the following:

Let( [ today = Get( CurrentDate ), one year ago = Date( Month( today ), Day( today ), Year( today ) - 1 ); almost one year ago = one year ago + 1, one year after = Date( Month( today ), Day( today ), Year( today ) + 1 ); almost one year after = one year after - 1; Franklin = Date( 1, 17,1706 ); Franklin 300 = Date( 1, 17, 2006 ) ]; Assert Equals( Years( today, today ), 0 ) & Assert Equals( Years( one year ago, today ), 1 ) & Assert Equals( Years( almost one year ago, today ), 0 ) & Assert Equals( Years( one year after, today ), 1 ) & Assert Equals( Years( almost one year after, today ), 0 ) & Assert Equals( Years( one year ago, “” ), 1 ) & Assert Equals( Years( almost one year ago, “” ), 0 ) & Assert Equals( Years( “”, one year after ), 1 ) & Assert Equals( Years( “”, almost one year after ), 0 ) & Assert Equals( Years( “”, “” ), 0 ) & Assert Equals( Years( Franklin, Franklin 300 ), 300 ) )

This test was written before the I started coding the function and as you can see it's both quite readable as a specification and not bad as an actual test (though I already see it can be enhanced).

Adding the tester to your application

To add the unit to your files:

  • Add a reference to the “Test” table in this file and name the table occurrence “Test”.
  • Add a layout for the new table occurrence and name it “Test: Form”. The name is case-sensitive.
  • Add yet another layout for the same table occurrence and name it “Test: List” (also case-sensitive). Make the layout viewed as list and set the size of layout parts:

    header: 26 pixels;
    body: 16 pixels;
    trailing grand summary: 16 pixels;
    footer: 16 pixels.

  • Optionally make yet another “Test: Help” layout.
  • Copy the form layout from this file and paste it to the new layout in your file.
  • Copy the list layout from this file and paste it to the new layout layout in your file.
  • If you’ve made “Test: Help”, copy the layout as well.
  • Optionally paint layout parts to your taste.
  • You’re done (except functions themselves, of course; you might want to copy at least the Assert Equals() function).

This instruction is repeated in the file itself, by the way. If you've done everything correctly, all buttons should work just fine.

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/t/trackback/510343/3283631

Listed below are links to weblogs that reference Develop custom functions more effectively using a simple test unit:

Comments

Thank you very much, Mikhail. This is brilliant stuff. Inspiring.

how did you type a tab character - I have been looking all over the web for this - did you hit a control sequence before you typed the tab button on the keyboard? I simply copied/pasted yours so you saved the day

Post a comment

If you have a TypeKey or TypePad account, please Sign In