Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Test-Driven Development (TDD) example


  • Please log in to reply
12 replies to this topic
guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011
This is an example of doing Test-driven development (wikipedia) in AutoHotkey. Test driven development (TDD) is the practice of writing test code first, then writing real code to pass your tests.

Problem:
A week ago, I came across a programming exercise to create a String Calculator using TDD. Since I don't understand TDD much at all, I watched some examples and decided to give it a shot myself (the beginner part). I completed it a few times since then, and today I recorded it. This was the result:

Watch my solution for the String Calculator here (10 minutes):
<!-- m --><!-- m -->
- use fullscreen
- forgive the AHK crashes, the java web screen recorder caused computer slowdowns
- feel free to pause, rewind, etc to follow with my thought processes


My Comments:

- I first learned about TDD maybe 6 months ago from the Bowling Game Kata. I did a little bit of reading but wasn't convinced that the benefits would outweigh the additional coding. But, there is a definite comfort in knowing that I could potentially have the freedom to completely rewrite code and ensure that the rewrite is correct if all the tests pass. This is good motivation to implement some type of testing into my personal project. But, since it wasn't designed with testing in mind right from the start, I doubt I'll be able to integrate this type of testing into my app. Besides, the global-friendliness of AHK also makes it hard, especially when I've taken no care in that regard. Maybe when AHK v2 is ready and I get my head around its objects I will be able to do it better.

- I don't like that my final solution included a repetitive "Loop, Parse" for both the sum and to check the negatives. In my first attempt at the kata, I put these together in one Loop. Later though, that didn't work when I tried to refactor the bits into their separate single responsibility functions.

- AHK has no exception handling, so I had to return -1 for failure and popup a messagebox with the error when negatives are inputted. Not very elegant. Are there alternatives?

- In the name of speed, I skipped some tests that should have been done that I had forgot. In previous run-throughs, I included these types of tests as well when testing the custom delimiter functionality. With the recorder going, I was feeling some pressure and forgot
Test_ShouldAllowCustomDelimiter() {
   assert( myadd("//;`n1;2"), 3)
   assert( myadd("//$`n1,2`n3$4") , 10)
   assert( myadd("//-`n1-2"), 3)
}

- Finally, running through this kata opened my eyes to just how slow I am when I write code. I am a decent touch typist, around 90wpm, but there were a few things that I learned to do as a result of this exercise:[*:kg3ilnc2]Get autocomplete and intellisense calltips working in Notepad++. This was a huge benefit, and will save me lots of time from going from editor to website help and back to editor. I tried majkinetor's ISense script but it was quirky for me. The autocomplete in Notepad++ works ok, but it slows the editor down a bit.
[*:kg3ilnc2]macro hotstrings! Being an AHK user I should be faulted for not using these sooner, but they are great, and never realized their usefullness. I created two hotstrings because of this exercise, one to close braces {`n} when I type the opening, and another to create a frame Test function. If anyone has other hotstring examples to help coding, I'm all ears :)- I used my build of majkinetor's UTest as the testing framework.

(for newcomers, registration is NOT required to reply, guest posting is allowed on the forum)

keybored
  • Members
  • 351 posts
  • Last active: Apr 26 2013 09:08 AM
  • Joined: 18 Jun 2006
Thanks! This example helped me understand TDD better. I have a project that will make a good learning exercise for me too.

I'm sure there are many others like me that only know some AHK and no other programming language. It makes it a challenge to learn different programming methodologies when the books about them are written in foreign languages.

guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011
cool, glad you liked it. feel free to post your processes as well. i have a hard time doing testing with my real world app, so it would be nice to see how someone else goes about it

keybored
  • Members
  • 351 posts
  • Last active: Apr 26 2013 09:08 AM
  • Joined: 18 Jun 2006
I just need a little help with setting up my first test. I can't remember all the variations I've tried and yes this is wrong.

; This should succeed if it the function outputs Jones,Bob
Test_First_Last() {
Assert_True(Test_NameParse("Bob Jones") = "Jones,Bob")
}

; my very basic function just to make sure I can get the testing to work
Test_NameParse(v_Name)
{
StringSplit, v_A, v_Name, %A_Space%
v_Result = %v_A2%,%v_A1%
Return v_Result
}
return

I'm creating a function to output the last name and first name (last,first) if it can be determined from a given full name input. For anything too vague it should error. Examples;

Bob Jones
Brown,Angela
Canterbury,Jenny B
Sandra J Campo
Mcintyre-Marriot,June
Joseph Hostetler

And these would fail since it's not possible to know for sure if those are middle names
Laurie Shepherd Rayford
Denise R. Araiza Loveless

guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011
ok well first you need to understand how UTest framework works.

you'll want to create a separate test.ahk file which will run your tests, and another ahk file that has your function code.

then, you can follow my example in the video, or read the UTest doc, but youll want your test file structure to be as follows:

first include the framework, then write your tests, then include your code. all your test functions with your assert statements should begin with the "Test_" prefix. but your actual function should not. so it should look something like this:

mytest.ahk
#include UTest.ahk

Test_yourTest() {
   result := NameParse("John Doe")
   assert_true( result="Doe,John" )
}

#include yourfunction.ahk

yourfunction.ahk
NameParse(string)
{
   ;// parse code
}


keybored
  • Members
  • 351 posts
  • Last active: Apr 26 2013 09:08 AM
  • Joined: 18 Jun 2006
It was just as you said. I fixed that and have two tests set up and working. Now I can add all the tests.

This is great. I can set up what I want to achieve and easily see if I break anything while I work on my code. I'm so used to testing one thing at a time and only after I find something strange happening. This will save a lot of debugging.

Big win for majkinetor and all his wonderful tools!

keybored
  • Members
  • 351 posts
  • Last active: Apr 26 2013 09:08 AM
  • Joined: 18 Jun 2006
guest3456,
Thanks again for this demonstrating. I'm slowly getting around to figuring it all out. Here are a couple examples for gui interaction functions in case anyone might find them useful.

The problem with some functions is you can hardly interrupt them to test failure (in normal operation). So I just ran a couple fuzzer scripts separately. I'm thinking of making an actual fuzzer tool, but this worked for my effort to learn how it worked.

Here are the functions I tested;
CharmapSetText(v_Text)
{
  IfWinNotExist, Character Map
  {
    ErrorLevel = 1
    Exit
  }
  ControlSetText, RICHEDIT50W1, %v_Text%, Character
  ControlGetText, v_Info, RICHEDIT50W1, Character
  if (v_Info = v_Text)
    ErrorLevel = 0
  if (v_Info <> v_Text)
    ErrorLevel = 1
}


FocusCharmap()
{
  WinWait, Character Map, , 10
  IfWinNotActive, Character Map, , 10 , 
    WinActivate, Character Map, , 10
  WinWaitActive, Character Map, , 10
  if (ErrorLevel = 1)
  {
    Return 1
    Exit
  }
  Sleep, 100
  ControlGetFocus, v_Control, Char
  if (v_Control = "RICHEDIT50W1")
    ErrorLevel = 0
  if (v_Control <> "RICHEDIT50W1")
    ErrorLevel = 1
}
return

These are the tests;
Test_SetTextTrue() {
CharmapSetText("ThisText")
Assert_True(ErrorLevel = 0)
}

Test_CharacterMapClosed() {
ifWinExist, Chara
  WinClose, Chara
FocusCharmap()
Assert_True(ErrorLevel = 1)
}

Test_CharacterMapOpen() {
Run, charmap
FocusCharmap()
Assert_True(ErrorLevel = 0)
}

Fuzzers;
Loop, 
{
ControlSetText, RICHEDIT50W1, asdfx, Character
}

Loop, 
{
WinWait, Untitled - Notepad, 
IfWinNotActive, Untitled - Notepad, , WinActivate, Untitled - Notepad, 
WinWaitActive, Untitled - Notepad, 
}


guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011
keybored,

first, good work starting your testing. now, i'm no expert on testing by any means. i'm just learning myself. but from my brief look, it seems like you're just testing whether the AHK commands work or not. for example CharmapSetText(), you are just using ControlSetText ahk command to put some text into a textedit box, and then setting ErrorLevel. and your test is simply testing whether ErrorLevel is as expected.

do you see? youre just testing whether the ControlSetText command worked or not. this might be useful if you have some weird edit controls that you think might not respond properly to text setting, but otherwise, i would probably just assume that those commands work until proven otherwise. i think its better to focus your testing efforts on testing the logic of your program, if that makes sense

keybored
  • Members
  • 351 posts
  • Last active: Apr 26 2013 09:08 AM
  • Joined: 18 Jun 2006
Oh I think it makes a little more sense now that you say that.

I do indeed have some weird edit controls. They are complicated due to sub-windows with unreadable window titles. So this was a run up to testing as I re-write and improve on functions for these controls.

I also have other people using this code and I'm hoping to prevent execution errors or interruptions. Who is the Patron Saint for automating? :wink:

Yet it is in fact just a function so what you are saying makes sense too. Testing throughout the flow of my code rather than just the functions. That makes sense. I very much appreciate the feedback!

guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011
yeah if your edit controls are weird, then its good to test that your text is getting correctly set. in the end, you can never do too much testing. but it is time consuming, so you have to weigh the tradeoffs

ill probably do another example in this thread at some point

keybored
  • Members
  • 351 posts
  • Last active: Apr 26 2013 09:08 AM
  • Joined: 18 Jun 2006
guest3456, Do you have any suggested reading on Test Driven Development? I notice from your other posts that you've done some reading on programming. I'm improving my programming skills and it looks like you are leading the way!

guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011
well, there is a bit of stuff out there, but actually i've never been fully satisfied with what i read. for me, since i'm a real beginner, it was better to see some examples and then try to imitate, for example, the Bowling Kata in the original post. here are a few other links:

<!-- m -->http://blog.extrache... ... d_tdd.html<!-- m -->

<!-- m -->http://cantgrokwontg... ... riven.html<!-- m -->

keybored
  • Members
  • 351 posts
  • Last active: Apr 26 2013 09:08 AM
  • Joined: 18 Jun 2006
Thanks. I enjoyed that and it is becoming more clear. True you can't write tests for things you haven't thought through.

I'm taking a week off. When I'm back it's going to be fun updating my projects with better functions and tests. Wait, that should be tests then functions.

Loop
{
Write Test
Fail
Pass
}