Time Clock Example


I'm writing a TimeClock application as an experiment. Here's some sample output:

  Timeclock> start 'misc'
  Timeclock> pause 
  Timeclock> start 'stqe' 
  Timeclock> jobs
  misc, started 02002/08/30  4:32 PM, is paused.
  stqe, started 02002/08/30  4:33 PM, is recording time.

I will, in this space, type up thoughts about how I might test TimeClock using this framework. -- BrianMarick


Getting Started

What you're looking at above is the Ruby interpreter, with prompts tweaked. Each of the commands is a normal top-level ruby method. They each return strings, which the interpreter has been told to print in a human-friendly way. A test framework can call those commands directly, capture the resulting string, and check it. That's what I do in my own acceptance tests, which use NathanielTalbott's xunit framework for Ruby.

Notice the date. I could have handled that in two ways:

  • used a MockObject to construct dates I could test using exact string matching.
  • relied on the fact that I'd already tested the date-formatting class. So I believe it will, if called correctly, return the right format date. However, I want to check that it was called correctly. I can do that by seeing if the date that comes back matches a regexp.

I did the latter. Is it reasonably common to use regexps to avoid checking expected results exactly? If so, how would I use them?

Note also that I have several lines of output, which I would like to split into substrings that I could talk about individually.

Now, for each of those strings, I'll be reluctant to type in the regexp again - that will be a hassle when I decide to change the output format, as I surely will.


Ward's Suggestion

Ward gives this a try without knowing anything about the real requirements ...

The clock has a display which list a number of things that are being timed. Initially the display is empty.

Display
job startDate() startTime() status()

The clock also has a convenient way to select jobs. It has the effect of entering a job code. A small number of buttons operate on the currently running job or the entered job.

Action
enter job misc
press start  
press pause  
enter job stqe
press start  

At this point the display has two jobs.

Display
job startDate() startTime() status()
misc 08/30 4:32 PM paused
stqe 08/30 4:33 PM recording time

Note: These tests use two fixtures to test one program. The Display fixture is an unordered RowFixture which compares results keyed on the field "job". The Action fixture is the generic ActionFixture. Both use TypeAdapter so that all parameter passing and validation is done with a ValueObject if possible, not a string.


Brian's Enlargement

I think this will work for the GUI version of the app, except that I'd be inclined to write the last table like this:

Display
job validDate() validTime() status()
misc true true paused
stqe true true recording time

The reason is that I trust (and verify, with UnitTests) the code to track dates correctly. Here, I just want to check that the ThinGUI is actually showing the right format date. If I were extra suspicious, I might have ValidDate() check that the date was either today or yesterday, and ValidTime() check that the given time was within the past hour or so. But I don't want to build specific times into this test.

Note that Ward's solution - one fixture to issue commands, another to accept results - also works for a command-line version. The "display" is the results returned by the last command. So we'd have something like this to invoke commands:

Command Arguments
start misc
pause
start stqe
jobs

Now we have a DisplayFixture to check the results of the last command:

Display
misc, started {{ValidDate}} {{ValidTime}}, is paused.
stqe, started {{ValidDate}} {{ValidTime}}, is recording time.

(I am supposing that the fixture constructs a regexp from the cell contents. I wouldn't want users to have to know about regexps, so everything outside of the squirrely brackets would be quoted. The things inside brackets would then be substituted in, so the first line would be tested against:

%r{misc, started \d\d/\d\d/\d\d \d\d:\d\d [AP]M, is paused\.} (That's Ruby syntax, but you get the idea. Note that the period is quoted.)

And I can continue the test with more commands:

(Here, the test would have a comment: "Notice that the 'misc' job, being the default, resumes when 'stqe' is paused.")

Command Arguments
pause
jobs

Display
misc, started {{ValidDate}} {{ValidTime}}, is recording time.
stqe, started {{ValidDate}} {{ValidTime}}, is paused.


Ward's Continuation

Brian continues his exploration and reports his experience online. I've read the October 25, 2002 installment which was very good. (See http://www.testing.com/cgi-bin/timeclock.pl.) Here I will follow along without doing any of the hard work. Instead I will write the tables that I believe a customer could write, though I wouldn't mind if a customer's helper wrote them either. In the following when I say "I" it will be in the role fo customer addressing "you", the development team. I'll use italics when I want to step out of these roles. -- WardCunningham

Here is what I'd like to do based on our conversations and my real need to have more than one background activity. First there is always a background activity, even if I don't specifiy one.

Action
press master reset clock  
enter date and time now 10/25/02 8:00 am
press start  
check billing to background

Now I want to show you how I want to enter new background tasks. First let's assume I have a few forground tasks already entered.

Tasks
task time to date billing rate
stqe 6 days 16:22 probono
usda 4:37 $450/hr

Here the customer is using a ColumnFixture to just specify setup data without doing any checks.

So here is how I'd like to use the "new task" window to specifiy a second background activity.

Action
press new task  
enter task name conference
enter billing rate $150/hr
enter task type background
enter task starts tomorrow
press ok  

You may have better ideas how to do this entry, but you get the idea. I'm thinking today, tomorrow and next monday would be sufficent choices for task starts right now.

So here is how this would look once entered. I'll do a little work today and more tomorrow. I'll cancel the new background mid day the next day.

Action
enter task stqe
press start  
work 4 hours  
press stop  
check task background
work 4 hours  
press off  
rest 16 hours  

This wold have to be a variation on the ActionFixture because it uses new action words, work and rest. See the Realtime fixture in the MusicExample to see how this might be done. Or there could be "fake" fields for work and rest that advance the clock wth lines like: | enter | work | 4 hours |

Ok, so that is the first day. Now the second.

Action
press on  
check task conference
enter task stqe
press start  
work 2 hours  
press stop  
check task conference
work 6 hours  
press off  
rest 16 hours  

Notice how the clock was already running the conference task when I turned it on. We'll have to discuss when a new day starts. Probably not midnight (I work past midnight all the time.) Probably not 8am (Sometimes I start before then, really.) Let's make the background day start at 4am until we think of something better.

(Note to self: add check for 4am here before the iteration is over.)

Now I'll quit the conference task in the middle of this next day.

Action
press on  
check task conference
work 4 hours  
enter task conference
press edit task  
enter task dispostion terminated
press ok  
work 4 hours  
press off  
rest 16 hours  

So that's three days of use. Here are the times I will have racked up in the interval.

Times
task total time total billing
background 8:00 0.00
stqe 6:00 0.00
usda 0:00 0.00
conference 10:00 1500.00

This is a RowFixture that looks directly at the task objects.

My background tasks stack up just like my foreground tasks. Here is a typical month. (Assume days are 8 hours, ha ha.) Focus on foreground is the percentage of a period that I'm working on the foreground. Divide it up however you like. I'll do the first week with some detail and then the next three a week at a time.

Background by Hour
hours foreground focus on foreground new backgound starts quit background
4 usda 75%    
4 stqe 25% conference today  
4 usda 75% childcare tomorrow  
4 stqe 75%    
8 usda 50%    
4 usda 25% on call monday  
4 usda 25%    childcare
8 stqe 50%    conference
40 usda 75%    
40 stqe 25% childcare today  
40 usda 25% conference today  

This is another ColumnFixture just specifying a scenario. Some columns could be added to provide running checks (and would be by the developers, if needed).

I'll put month end totals here when I get to them. If you put something in, please note that they are your totals, because I think there might be some situations I want to think through as I add them up.

And so ends our fantasy specification from a wise and confident customer. The key thing to notice is that she moved up and down levels of abstraction as she would in a conversation. She knows that she is making fixturing work as she does but she is happy to pay for that too.

 

Last edited October 27, 2002
Return to WelcomeVisitors