Post #101,092
5/9/03 12:53:25 PM
|
Apparently my question sounded dumber than it actually was
I didn't mean to imply that you just ground out code without testing.
I'm always on the lookout for tips and tricks from the professionals that I can pass on to my students, particularly those in their second or third term where they have a good grasp of basic syntax and grammar but lack skill and practice in implementation.
For example, a typical newbie problem is trying to write the entire program all at once instead of phasing in functionality. I have several students right now who are in the habit of doing this and it's taking quite a few sessions of working through problems with them to get this across.
Another phrase I pass on is "Be prepared to throw all the code out and start over."
Recently a student showed me his function for swapping the values of two integer variables:
void swap_int(int& x, int& y) { x = y; y = x; return; }
It took me several moments to realize that he wasn't kidding. Then I took him by the hand to the whiteboard with a couple of examples to illustrate why it wasn't working the way he expected.
Granted, his story is atypical. But I think it shows that good coding practice is much more than just knowing the syntax of a language. I'm trying to incorporate examples of good practice in my lessons and talking to people like you who code for a living helps a lot.
Tom Sinclair
"Man, I love it when the complete absence of a plan comes together." - [link|http://radio.weblogs.com/0104634/|Ernie the Attorney]
|
Post #101,093
5/9/03 1:00:45 PM
|
Lessons to be gleaned:
- Don't write the whole thing before you start testing.
- When you find a bug, add the test first, verify it fails, then fix until it no longer fails. That way you know you're actually fixing the bug, and not just adding another test that would pass anyway.
- Use a framework that gets out of the way. Testing will get easier and easier, to the point where it's actually fun. I recommend JUnit and friends; the API is very similar across a gamut of languages. We use the Java and PL/SQL frameworks here; we don't use the C++ framework only because we already had one.
- Rerun ALL the tests after EVERY change. This is where a unit test becomes a regression test. You want to make sure you didn't break other things by association.
- Testing enhances maintainability. If you can change something and immediately know if you broke something else, then you're more likely to have confidence in your changes.
I'll add more as I think of it. :-)
Regards,
-scott anderson
"Welcome to Rivendell, Mr. Anderson..."
|
Post #101,094
5/9/03 1:09:15 PM
|
Great stuff!
What about your definition of a 'unit' for testing?
In other words, what amount of code is too small to bother with having its own unit test?
How much code is too big for just one test?
I think you've already established one metric: "One method, one test".
I knew about JUnit already, but can you recommend some similar frameworks for C++? (We start our students out with C++ before moving them to other languages.)
Tom Sinclair
"Of course, just because we've heard a spine-chilling, blood-curdling scream of the sort to make your very marrow freeze in your bones doesn't automatically mean there's anything wrong." -- (Terry Pratchett, Soul Music)
|
Post #101,095
5/9/03 2:04:17 PM
|
Re: Great stuff!
[link|http://www.xprogramming.com/software.htm|The mother lode of testing frameworks].
I do a lot of Java unit testing. Here's how I break it down:
1) One matching unit test per class. If the class is Foobar.java, then I have a FoobarTest.java.
2) One suite of tests per package. The suite is a Java unit test that can group other unit test classes. If the package were com.spork.stuffies, with a Foobar.java and a Moobar.java, then I'd have FoobarTest.java, MoobarTest.java, and a StuffiesSuite.java that simply incorporates the FoobarTest and MoobarTest. The tests would all go into the com.spork.stuffies.test package.
3) Within a unit test, I have a set of test methods. The breakdown varies depending on functionality; typically I will group them by function; ie. in the list test class I have a testCreate to test the constructors, testParse to test the parsing, etc. Within each of those methods I will have a series of "make this call, then test the results" blocks. I make use of unique descriptive strings passed with each assert to be able to nail down where a particular test method fails.
4) The need for setup and teardown will affect the number and kind of methods in the unit test class as well. If there is a fair amount of setup, and it has to be done fresh before a test, then that test should have it's own method. The setups and teardowns get run before each test method in the test class.
Regards,
-scott anderson
"Welcome to Rivendell, Mr. Anderson..."
|
Post #101,241
5/10/03 10:16:03 PM
|
C++ unit testing - sounds painful
I mean, the typical failure mode for a C++ program is an unrecoverable core dump. I guess you can use it to test logic errors but you're only getting some of the benefits.
I have to say that the SUnit framework that we are using in Squeak these days is FAB! You run the tests and it puts up a list of failures in a window. Click a failure and the code is rerun and a debugger opens on the entry point for the test. You step in, watch what is going on, fix it as you go, and move on. Coding in the debugger is fun!
"Packed like lemmings into shiny metal boxes. Contestants in a suicidal race." - Synchronicity II - The Police
|
Post #101,099
5/9/03 2:14:49 PM
|
More:
Regards,
-scott anderson
"Welcome to Rivendell, Mr. Anderson..."
|
Post #101,115
5/9/03 3:33:25 PM
|
Code coverage
I seem to remember tools for this as well. TCov/Gcov?
|
Post #101,103
5/9/03 2:45:32 PM
|
How does JUnit (and the like) deal with web pages?
Most of what we write is web pages served up by PHP. Does JUnit allow you to define simulated HTTP POST/GET actions? Does it have a way of running PHP functions that are not called by the web server? (Later versions of PHP work from the command line so this will be less of an issue.)
I know these questions are fairly specific to PHP, which I know you don't do much (at all?). But I have zero experience with testing tools, so don't understand how they can be used to test individual functions within a PHP script.
===
Implicitly condoning stupidity since 2001.
|
Post #101,104
5/9/03 2:50:06 PM
|
Not really specific to PHP.
JUnit is a unit testing framework for Java code.
There is a Java framework called [link|http://httpunit.sourceforge.net/|HttpUnit], however, that can do GET, POST, manage cookies, parse HTML, etc. So HttpUnit in conjunction with JUnit is pretty good for testing web pages.
And in fact, looking at that page now for the first time in a few months I see that HttpUnit now [link|http://httpunit.sourceforge.net/doc/Javascript-support.html|supports some Javascript].
Regards,
-scott anderson
"Welcome to Rivendell, Mr. Anderson..."
|
Post #101,107
5/9/03 3:05:10 PM
|
Also from that page
Didn't see that link until after I posted, but there's [link|http://phpunit.sourceforge.net/|PHPUnit]. The docs are kind of sparse. It's hard to tell what are the example class names, and which are the actual PHPUnit classes, but I suppose that will make more sense once I have it installed.
===
Implicitly condoning stupidity since 2001.
|
Post #101,110
5/9/03 3:08:18 PM
|
HttpUnit is more useful for integration testing.
I've also used portions of it to simulate a web browser in 'screen-scraping' type applications.
Regards,
-scott anderson
"Welcome to Rivendell, Mr. Anderson..."
|
Post #101,184
5/10/03 12:17:17 AM
|
We use PHPUnit where I work.
If you do any kind of object programming in PHP, it will not be hard to figure out how to use it. We have one file per test class with multiple Test* methods that test things. Just be aware that you can't leave intermediate stuff hanging around between tests - it creates and destroys the test instance for each test.
Wade.
Is it enough to love Is it enough to breathe Somebody rip my heart out And leave me here to bleed
| | Is it enough to die Somebody save my life I'd rather be Anything but Ordinary Please
| -- "Anything but Ordinary" by Avril Lavigne. |
|
Post #101,146
5/9/03 5:30:28 PM
|
I'm experimenting with testing dynamic content ...
When you start using TDD seriously, your coding style changes to accomodate testing. Adding tests to an existing code base is hard. Writing tests as you go is easy. So, the way you do dynamic content on a web page may change to facilitate the abiliity to test. I've not done any PHP work (so I'm not sure how applicable this is to your situation), but I began experimenting with some dynamic content test first approach. Here's what I'm trying out ... First you separate your static page data from the dynamic part. We don't really care about testing the static part (why? ... ummm ... because it's static). We do care about making sure the dynamic content is correct however. I choose to use a template system for the static content. The dynamic part choses the template and defines a set of name/value pairs used to populate the template based upon the input to that web page. Then my unit tests just exercise the dynamic portion and make sure that the proper name/value pairs are supplied with the desired template. Example ... \n class LoginPage < Webpage\n def update(cgi)\n if valid_password(cgi['account'], cgi['password'])\n next_page = select_template('start_screen')\n next_page['account'] = cgi['account']\n else\n next_page = select_template('login_screen')\n next_page['msg'] = 'invalid login, try again'\\\n end\n return next_page\n end\n end And the unit test might look like ... \n class LoginPageTest\n def test_good_login\n page = LoginPage.new\n cgi = {'account' => 'jim', 'password' => 'goodpassword'}\n next_page = page.update(cgi)\n assert_equal "start_screen", next_page.template_name\n assert_equal "jim", next_page['account']\n end\n\n def test_bad_login\n page = LoginPage.new\n cgi = {'account' => 'jim', 'password' => 'boguspassword'}\n next_page = page.update(cgi)\n assert_equal "login_screen", next_page.template_name\n assert_match /invalid login/, next_page['msg']\n end\n end That's just a sketch of what I'm trying. I've glossed over some details. Any feedback is welcome.
-- -- Jim Weirich jweirich@one.net [link|http://w3.one.net/~jweirich|http://w3.one.net/~jweirich] --------------------------------------------------------------------- "Beware of bugs in the above code; I have only proved it correct, not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)
|
Post #101,125
5/9/03 4:15:07 PM
|
Can I quote this thread?
I'd like to take the tips/tricks/best practice suggestions from this thread and compile them into a handout for my students.
If you've replied already or are planning to reply to this thread, please let me know if you'd rather I not use your remarks.
I'll edit a bit to eliminate off-topic, non-technical points and if you wish, I can post the compilation for approval here.
I'm pushing to have our programming curriculum include more real-world best practice topics and this would be a great start. My program director agrees with me and now it's a matter of selling it to the other faculty and the students.
Tom Sinclair
"Man, I love it when the complete absence of a plan comes together." - [link|http://radio.weblogs.com/0104634/|Ernie the Attorney]
|
Post #101,126
5/9/03 4:21:19 PM
|
Even better:
Compose your compilation, and post it on [link|http://twiki.iwethey.org/twiki/bin/view/Main/|TWikIWETHEY]. :-)
But yes, you have my permission. The more people using testing, the better.
Regards,
-scott anderson
"Welcome to Rivendell, Mr. Anderson..."
|
Post #101,139
5/9/03 5:10:44 PM
|
No, you can't use my comments
After all, it's not like I posted them to a publicly accessible place or anything. Oh wait ...
[image|/forums/images/warning.png|0|This is sarcasm...]
===
Implicitly condoning stupidity since 2001.
|
Post #101,165
5/9/03 8:43:43 PM
|
Thanks!
I'll see what I can put together this weekend.
Tom Sinclair
"Man, I love it when the complete absence of a plan comes together." - [link|http://radio.weblogs.com/0104634/|Ernie the Attorney]
|
Post #101,137
5/9/03 5:09:24 PM
|
A TDD Example
tjsinclair: I'd like to take the tips/tricks/best practice suggestions from this thread and compile them into a handout for my students.
If you are interested, I have a laboriously detailed account of a pair programming session at one of our XP user group meetings. Lots of test-first design examples and some refactorings are given. Feel free to use whatever you need.
Here's the URL: [link|http://w3.one.net/~jweirich/talks/tdddemo/index.html|http://w3.one.net/~j...dddemo/index.html]
(Note: that URL should be good until mid month when my ISP upgrades me from 100 Megabytes to 5 Megabytes of web storage. Needless to say, I'm working on getting a new site, but it won't be ready for a week or two).
-- -- Jim Weirich jweirich@one.net [link|http://w3.one.net/~jweirich|http://w3.one.net/~jweirich] --------------------------------------------------------------------- "Beware of bugs in the above code; I have only proved it correct, not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)
|
Post #101,239
5/10/03 10:01:19 PM
|
Ooh, he left out the ^
and a line
void swap_int(int& x, int& y) { x ^= y; y ^= x; x ^= y; }
"Packed like lemmings into shiny metal boxes. Contestants in a suicidal race." - Synchronicity II - The Police
|
Post #101,253
5/10/03 11:37:38 PM
|
That's just evil to the core. :-)
|
Post #101,316
5/11/03 5:43:45 PM
|
It's C++, what do you expect?
"Packed like lemmings into shiny metal boxes. Contestants in a suicidal race." - Synchronicity II - The Police
|
Post #101,413
5/12/03 8:43:53 AM
|
I would only use that if it were properly commented.
Is it enough to love Is it enough to breathe Somebody rip my heart out And leave me here to bleed
| | Is it enough to die Somebody save my life I'd rather be Anything but Ordinary Please
| -- "Anything but Ordinary" by Avril Lavigne. |
|
Post #101,435
5/12/03 10:35:20 AM
|
I'd probably never use it at all
Its a cool parlor trick I think. Thats about it.
"Packed like lemmings into shiny metal boxes. Contestants in a suicidal race." - Synchronicity II - The Police
|
Post #101,462
5/12/03 1:08:32 PM
|
Its a cool parlor trick...
...that has an annoying tendancy to show up on the [insert correct TLA here; the name of the test that you have to take to get into a Master's program] test.
(This gettin old just isn't what it's cracked up to be...)
jb4 "We continue to live in a world where all our know-how is locked into binary files in an unknown format. If our documents are our corporate memory, Microsoft still has us all condemned to Alzheimer's." Simon Phipps, SUN Microsystems
|
Post #101,466
5/12/03 1:25:21 PM
|
Not even really a C++ issue as such, is it?
You can do that evil thing he did in ANSI C with pointers. Have you seen this? \nregister n = (count + 7) / 8; /* count > 0 assumed */\n\n switch (count % 8)\n {\n case 0: do { *to = *from++;\n case 7: *to = *from++;\n case 6: *to = *from++;\n case 5: *to = *from++;\n case 4: *to = *from++;\n case 3: *to = *from++;\n case 2: *to = *from++;\n case 1: *to = *from++;\n } while (--n > 0);\n }\n Now THAT is evil!
-drl
|
Post #101,565
5/12/03 8:50:05 PM
|
Its a Duff Device
and as an optimization for moving chunks of data rapidly its pretty good.
"Packed like lemmings into shiny metal boxes. Contestants in a suicidal race." - Synchronicity II - The Police
|
Post #101,594
5/12/03 11:25:43 PM
|
Yep - it's assembler in C
-drl
|
Post #101,603
5/13/03 12:25:46 AM
|
Most ASM Languages have a SWAP instruction.
|
Post #101,651
5/13/03 11:18:05 AM
|
Yes, not my point
My point was that C is syntactically capable of these kinds of weird things you might use at a very low level.
-drl
|
Post #101,657
5/13/03 11:27:46 AM
|
And my point...
... (if there's really a point to be made) is that this is even lower level than assembly language (unless you count the One Instruction Machine). More like programming ASIC's. :-)
|
Post #101,654
5/13/03 11:20:14 AM
|
SWAP?
SWAP? Did you mean MOVB/MOVW?
-- -- Jim Weirich jweirich@one.net [link|http://w3.one.net/~jweirich|http://w3.one.net/~jweirich] --------------------------------------------------------------------- "Beware of bugs in the above code; I have only proved it correct, not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)
|
Post #101,853
5/14/03 1:45:11 PM
5/14/03 1:46:33 PM
|
That's ANSI C?!?
ANSI C compilers I've run across recently wouldn't allow you to put a case label inside of a block (i.e. inside of an open curly brace). I know ANSI C++ won't let you...
jb4 "We continue to live in a world where all our know-how is locked into binary files in an unknown format. If our documents are our corporate memory, Microsoft still has us all condemned to Alzheimer's." Simon Phipps, SUN Microsystems
Edited by jb4
May 14, 2003, 01:46:33 PM EDT
|
Post #101,854
5/14/03 1:56:36 PM
5/14/03 1:58:08 PM
|
That's ANSI C?!? ... YES
I just tried it undef GNU C++, GNU C and Solaris C++. Duff's device works fine under all of them.
(NOTE: I had to change "register n" to "register int n" for it to work in C++).
-- -- Jim Weirich jweirich@one.net [link|http://w3.one.net/~jweirich|http://w3.one.net/~jweirich] --------------------------------------------------------------------- "Beware of bugs in the above code; I have only proved it correct, not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)
Edited by JimWeirich
May 14, 2003, 01:58:08 PM EDT
|
Post #101,859
5/14/03 2:22:19 PM
|
Re: That's ANSI C?!?
No, it's antsy C!
Took me a while to figure out what the hell it was for the first time I saw it.
-drl
|