Page 2 of 3
Posted: Fri Feb 17, 2006 3:39 am
by Maugrim_The_Reaper
I think the bare bones approach is fairly valid. I use unit tests, I like unit tests, but it takes a lot of willpower (or maybe a dense brain

) to get over the time needed to implement them when starting to learn their use. I think this is probably the main block to more widespread adoption of unit testing. There are still guys out there working on stuff from Java, Perl, and who knows where else all because they can't take the time investment unit testing takes up. When you see Perl's Test::More getting ported to PHP its a sign Unit Testing is being resisted. All the Agile Dev hype, yet still not much of an impact in PHP for regular users...
I must admit, it doesn't help when one of the most popular PHP unit testing tutorials (Simpletest Log tut) fails under the current SimpleTest releases since its out of date. No doubt Marcus is really busy (how many projects does the guy run again?) but I hear about that almost as much as the time investment issues.
I think the simple UT above by aborint is pretty good. I think any testing is good anyways, but a UT similar method at least lowers the barrier for new testers to get started and see what testing (even without the whole hog Simpletest/PHPUnit frameworks) is about and why it works.
Posted: Fri Feb 17, 2006 2:46 pm
by Christopher
Maugrim_The_Reaper wrote: but a UT similar method at least lowers the barrier for new testers to get started and see what testing (even without the whole hog Simpletest/PHPUnit frameworks) is about and why it works.
Hey thanks for the comments. I agree, that's why I wanted to do something really simple to give unit testing newbies a taste. I have found that once a unit test finds a bug for you, you are a little hooked. But I also agree that the pain of creating good test coverage for a real project is miserable. Unfortunately the people who create the unit testers are test junkies who seem to enjoy the pain.
I took a look at my code an thought I would simplify it even more by combining the unit tester and the reporter. It would almost always be used that way anyway. The all in one design means you only need to create one object -- the tester. If you wanted to do different reporting you could just inherit the UnitTester. I renamed things a little to make it more like other unit testers, and exposed pass() and fail() methods.
Code: Select all
class UnitTester {
var $_passes = 0;
var $_fails = 0;
var $_test_stack = array();
var $_trace_pos = 0;
function assertTrue($result) {
if ($result) {
$this->pass();
} else {
$this->_trace_pos = 1;
$this->fail();
}
}
function assertFalse($result) {
if ($result) {
$this->_trace_pos = 1;
$this->fail();
} else {
$this->pass();
}
}
function pass() {
++$this->_passes;
}
function fail() {
++$this->_fails;
$test_data = debug_backtrace();
$this->_test_stack[] = $test_data[$this->_trace_pos];
$this->_trace_pos = 0;
}
var $passed_heading_style = 'color: white; background-color: green; margin-top: 2; padding: 4 4 4 4;';
var $failed_heading_style = 'color: white; background-color: red; margin-top: 2; padding: 4 4 4 4;';
var $failed_test_style = 'color: white; background-color: red; margin-top: 2; padding-left: 4;';
function run() {
if ($this->_fails > 0) {
echo '<div style="' . $this->failed_heading_style . '">Failed ' . $this->_fails . ' of ' . ($this->_fails + $this->_passes) . ' tests. </div>';
$n = 1;
foreach ($this->_test_stack as $this_data) {
$message = $n++ . ". Failed: {$this_data['function']} on line {$this_data['line']} of file {$this_data['file']}. ";
echo '<div style="' . $this->failed_test_style . '">' . $message . '</div>';
}
} else {
echo '<div style="' . $this->passed_heading_style . '">Passed ' .$this->_passes . ' tests.</div>';
}
}
}
And to run it:
Code: Select all
include 'TinyTest.php';
$tester = new UnitTester();
// these tests pass
$tester->assertTrue(1 == 1);
$tester->assertFalse(1 == 2);
// these tests fail (comment them to see the passed output)
$tester->assertTrue(1 == 2);
$tester->assertFalse(1 == 1);
$tester->run();
Do you think this simpler one is better? Or the first one with a separate reporter? Any comments or improvements?
Posted: Fri Feb 17, 2006 5:26 pm
by Ambush Commander
If this is going to be a temporary solution, I think keeping it as simple as possible is a good thing. I noticed, that you omitted assertEqual(). Why?
Posted: Fri Feb 17, 2006 6:18 pm
by Christopher
Ambush Commander wrote:If this is going to be a temporary solution, I think keeping it as simple as possible is a good thing. I noticed, that you omitted assertEqual(). Why?
If you look at unit testers they usually only provide assertTrue() and assertFalse() at the lowest level, then build all the other asserts on top of these. I left is out because:
Code: Select all
$tester->assertEqual(1, 1);
// is the same as
$tester->assertTrue(1 == 1);
Posted: Fri Feb 17, 2006 8:30 pm
by lastcraft
Hi...
Maugrim_The_Reaper wrote:
I must admit, it doesn't help when one of the most popular PHP unit testing tutorials (Simpletest Log tut) fails under the current SimpleTest releases since its out of date.
Point taken! I am planning a major tutorial and doc. rewrite in the run up to the 1.0.1 release.
It's a poor excuse, but SimpleTest kind of caught me out. If I ever got 300 downloads a month I was going to be pretty happy

. I've been struggling to catch up ever since, mostly with a constantly changing PHP syntax and minutia of HTTP.
Maugrim_The_Reaper wrote:
No doubt Marcus is really busy (how many projects does the guy run again?) but I hear about that almost as much as the time investment issues.
Nag me again in a month or two. I'm actually cutting back on projects, and forever looking for help with the ones I've got, but I'll be supporting SimpleTest as long as it has a user base. That includes the surrounding tutorials.
Regarding TinyTester, how about stripping it down some more...?
Code: Select all
<?php
ensure(strtolower('Hello') == 'hello');
?>
I am wondering if you cannot let the destruction of a global object could trigger the totals at the end, and the construction could start the page...
Code: Select all
function ensure($condition) {
static $tester;
if (! $tester) {
$tester = new UnitTester();
}
$tester->ensure($condition);
}
class UnitTester {
function __construct() { ... } // Paint header
function __destruct() { ... } // Paint footer
function ensure($condition) { ... } // Adapts to HTML or CLI.
}
I am nowhere near a PHP box to test this, so you might not be able to print anything as the program shuts down. If it works then group tests are just includes.
yours, Marcus
Posted: Fri Feb 17, 2006 8:51 pm
by Christopher
Hi Marcus,
lastcraft wrote:I'm actually cutting back on projects, and forever looking for help with the ones I've got, but I'll be supporting SimpleTest as long as it has a user base. That includes the surrounding tutorials.
Any project that need a specific push? I'd be interested. I have thought about
lamplib recently, but I am on hold on the general lib front until the Zend Framework is released.
lastcraft wrote:Regarding TinyTester, how about stripping it down some more...?
I am nowhere near a PHP box to test this, so you might not be able to print anything as the program shuts down. If it works then group tests are just includes.
I'd be interested in you input -- obviously. I really had two goals for this thing:
- First to have something that is a stepping stone to SimpleTest/PHPUnit. I think if testing newbies could see a test find a bug they would get interested in real unit testing. I tried to make the experience similar to using SimpleTest to expidite the transition.
- Second was to have something that was so minimal that you would post it with code in forums to provide a "zero install" test framework as a companion to code.
Posted: Mon Feb 20, 2006 9:29 am
by Maugrim_The_Reaper
Nagging appointment noted

Posted: Mon Feb 20, 2006 10:27 am
by Weirdan
timvw wrote:arborint wrote:
It seems like there needs to be a tool that is almost like a macro-recorder (like record mode in expect) where you go to the pages and it records what they are supposed to look like and what is supposed to be done.
Something like
http://www.pushtotest.com/Downloads/features.html would be nice

To me it seems that for user interaction tests
Selenium with
Selenium Recorder is almost ideal.
Posted: Sat Apr 22, 2006 11:24 pm
by alex.barylski
Thats what is meant by a unit test...
Ahhhhh...
I have never used any unit test software in PHP, but have extensive experience using debugging features in VC++...
It looks like what you are emulating is a simple MFC ASSERT macro...
But instead of inling with your production code, you use a separate system with which you can test your objects, functions, etc...?
Thus keeping your retail code clear of the convoluted test code?
I prefer this method (MFC ASSERTS that is) as you avoid having to work with a third party system, in this case a unit test system/framework, whatever...
It's like using phpDocumentor to write docs instead of firing up Word everytime you add a function, change a function, etc...
Speeds up development...IMHO
What else does a unit test framework do?
Cheers

Posted: Sun Apr 23, 2006 12:22 am
by John Cartwright
Don't think there is such as thing as a Unit Testing framework, although that may depend on your definition of a framework.
Testing the basically emulating possible (if not all) scenarios pertaining to specific code blocks. True testing guru's actually write the tests first, atleast that's what I've seen in my time, and write the code second. You add a couple lines of code -- re-run the test. If it passes, your golden! Not to mention if you are upgrading your codebase, you may be unaware that the line of code you just commented out was a bug fix for something else, a test would light the bug up as clear as christmas lights.
When starting a test (using TinyTester)
we would start off a method like
Code: Select all
class testingClass
{
function isACoolMethod()
{
return true;
}
}
$tester = new UnitTester();
$tester->assertTrue(testingClass::isACoolMethod());
$tester->run();
and we would test the code,
As we progressively are adding code to this method, we are continually runnign tests to make things are working as expected. This is also useful to make sure unexpected values are caught appropriatly. This next quick test will make sure we are handling invalid data properly.
Code: Select all
class testingClass
{
function isACoolMethod($number)
{
return is_numeric($number);
}
}
$tester = new UnitTester();
$tester->assertTrue(testingClass::isACoolMethod(4));
$tester->assertTrue(testingClass::isACoolMethod(5));
$tester->assertFalse(testingClass::isACoolMethod('fail'));
$tester->assertFalse(testingClass::isACoolMethod('3423fail'));
$tester->run();
It obviously gets much much much more complicated than this, although I hope that clears some things up.
Posted: Wed Apr 26, 2006 1:11 am
by Christopher
Hockey wrote:What else does a unit test framework do?
Probably the most interesting thing is Mocks. That's where Test Driven Development starts to make sense when you create Mocks of classes you haven't build yet (but generally know the interface of). That allows you to start building the classes with dependencies first, rather than building the plumbing first.
Posted: Fri Apr 28, 2006 2:53 am
by alex.barylski
arborint wrote:Hockey wrote:What else does a unit test framework do?
Probably the most interesting thing is Mocks. That's where Test Driven Development starts to make sense when you create Mocks of classes you haven't build yet (but generally know the interface of). That allows you to start building the classes with dependencies first, rather than building the plumbing first.
While I agree that establishing a solid interface first is important, determining dependancies before implementation...
I dunno...
by depedancies you mean, it's reliance on other objects, includes, etc???
If so, then dependencies are more of a side effect of your implementation...in which case I would *personally* find that redundant and a time waste...
Also, I always try to keep dependencies to a minimum...passing objects in via function arguments if I can, etc...including required headers, etc in the caller's module as opposed to hard coding a path inside my classes implementation file...
Am I missing something?
Cheers

Posted: Fri Apr 28, 2006 6:23 am
by Maugrim_The_Reaper
Dependencies also have APIs (usually). Mocks were a Big Deal when I first came across them.
Posted: Fri Apr 28, 2006 11:59 am
by Christopher
Hockey wrote:While I agree that establishing a solid interface first is important, determining dependancies before implementation...
I dunno...
Determining depencencies is one of the foundations of software design. I don't see how you could produce a design without knowing what the dependencies are.
Posted: Sun Apr 30, 2006 5:51 pm
by silicate
Hey there,
I like the idea of the tiny tester - especially the input from Marcus about having a single method call rather than asserts. I would suggest calling it expect though since tests are meant to test your expectations. I am wondering though if this isn't approaching the functionality of
assert with a callback defined. I actually use assertions in some of my class contructors to ensure that my expectations about the parameters are met.
Code: Select all
public function __construct($less, $more) {
assert("$less < $more");
} // end constructor
This is handled by a callback that writes to a debug log and displays no output to the browser. It's kinda cool in that you can turn off the assertions and still have full error and exception support. Maybe if the assertions were enabled by the unit tests and then the callback was used as the entry point similar to the way that Marcus was using it.
Of course I think that your target audience it definitely newbie TDD coders. I personally would get frustrated without being able to Mock up objects that are already tested. For me, the beauty of mocks is that you can have an object that is incredibly complex to instantiate and not worry about it. Using Mock Objects as critics is the best as can merely set up the expected results of method calls and treat it as a little black box because you have tested it's internals in another unit test.
That being said, there are some issues with SimpleTest and type hinting that are sometimes a real pain, but then just handwriting a Mock object is good enough to get your through in most cases. The expectations are something I always have to have on hand since the parameter order seems weird
Testing is as harder than coding in my opinion, since that's where the real design part comes into play. Tests make you think about what you (and your client) want to happen, coding is just pushing data around with maybe a transformation applied on the way. The dependencies - or preferably the relationships - between objects is what gives OOP power.
later,
Matthew.
PS: sup arborint
