Page 1 of 2
Help getting started with unit testing
Posted: Thu Apr 17, 2008 12:37 pm
by RobertGonzalez
Okay, so I am ashamed to say that I have never really used unit testing in any major or productive manner. I need to start. It is the right thing to do.
But I don't know where. However, I do have a motivating question that was posed to me the other day and I would love if someone would be willing to put a little time into showing me the light.
Scenario: You have an interface called Shape with a method name getArea. There are two classes that implement Shape: Rectangle and Circle.
Code: Select all
<?php
interface Shape {
public function getArea();
}
class Rectangle implements Shape {
public $width;
public $height;
public function getArea() {
return $this->width * $this->height;
}
}
class Circle implements Shape {
public $radius;
public function getArea() {
return M_PI * $this->radius * $this->radius;
}
}
?>
Barring the known missing elements of these classes (like setting the dimensions and what not), what unit tests would I want to start with when developing unit test cases for this set up?
Any help is much appreciated.
Re: Help getting started with unit testing
Posted: Thu Apr 17, 2008 12:59 pm
by Christopher
I am not the most test infected around here -- especially in the TDD area. From what you wrote, I am assuming that you are not doing TDD. Instead you want to write unit tests to both prove that these classes/methods work correctly and maybe more importantly make sure they keep working as you make changes to them down the road.
These are pretty simple test cases. You would just set values and see if tests pass. But because you are using public properties, you will also want to set values that are not numbers -- like null or "foo". I find one of the best things about writing unit tests (again not TDD) is that it makes you think about things like this that you don't think about when you are focused on the implementation instead of the results.
Code: Select all
class ShapesTest extends UnitTestCase {
function testRectangle() {
$rectangle = new Rectangle();
$rectangle->width = 2;
$rectangle->height = 4;
$this->assertEqual($rectangle->getArea(), 8 );
$rectangle->width = null;
$rectangle->height = 'foo';
$this->assertEqual($rectangle->getArea(), /* Maybe 0? NULL? */ );
}
}
You didn't say which test framework you are using.
Re: Help getting started with unit testing
Posted: Thu Apr 17, 2008 1:05 pm
by RobertGonzalez
Thanks arborint.
Let's say the testing suite is up to you. And you are right, this is not for TDD so much as it is for just plain Jane test case development.
Would type testing be appropriate here (is that even something a developer should test for)?
Re: Help getting started with unit testing
Posted: Thu Apr 17, 2008 1:59 pm
by Christopher
Everah wrote:Let's say the testing suite is up to you. And you are right, this is not for TDD so much as it is for just plain Jane test case development.
I recommend SimpleTest because I think it is the right kind of implementation, I think it is growing in the right directions, and I think the people behind it are the right sort of chaps ...
Everah wrote:Would type testing be appropriate here (is that even something a developer should test for)?
Yes. I am not a gung-ho for TDD as others are, but I find that unit testing quickly becomes TDD because it makes you think about what your code is actually doing. It this case it quickly became clear to me that bad property values were a weakness in these classes. I started thinking about maybe using __set() so I could keep the interface but disguise the checks, etc. etc.
Type testing is appropriate I think. However I don't know what code is around these classes. If they are only used internally by other classes which validate that the values are integers/floats as appropriate -- then no need to check or test for that. If not then the classes need more code and the you need test to check for those error inputs.
Re: Help getting started with unit testing
Posted: Thu Apr 17, 2008 2:00 pm
by arjan.top
maybe it will sound stupid but does 0? NULL? do? Never used it ...

Re: Help getting started with unit testing
Posted: Thu Apr 17, 2008 2:09 pm
by Christopher
arjan.top wrote:maybe it will sound stupid but does 0? NULL? do? Never used it ...

Sorry ... that's not really valid PHP.

I was just putting in some values because I did not know what Everah wants the function to return if it gets bad width or height values. I meant "Maybe it will return 0? Or maybe it will return NULL?"
Re: Help getting started with unit testing
Posted: Thu Apr 17, 2008 2:13 pm
by arjan.top
Now i feel even more stupid

Re: Help getting started with unit testing
Posted: Thu Apr 17, 2008 5:22 pm
by georgeoc
I would go one stage further than arborint's example for your first step. Take a leaf out of the BDD book, and make sure your methods (i.e. test cases) have names which describe the desired functionality. So, requirements like this:
Code: Select all
Rectangle requirements:
- it should return the correct area for two simple integers
- it should throw an error when width or height are not numeric
Circle requirements:
- it should ... etc.
translates to these tests, separated into areas of functionality:
Code: Select all
class RectangleTest extends UnitTestCase {
function setUp() {
$this->_rectangle = new Rectangle();
}
function testItShouldReturnCorrectAreaFor2SimpleIntegers() {
$this->_rectangle->width = 2;
$this->_rectangle->height = 4;
$this->assertEqual($this->_rectangle->getArea(), 8);
}
function testItShouldThrowErrorWhenWidthIsNotNumeric() {
try {
$this->_rectangle->width = 'foo';
$this->fail('Should not get to this point');
} catch (Exception $e) {
$this->assertPattern('~numeric~', $e->getMessage());
}
}
}
class CircleTest extends UnitTestCase {
//etc...
}
Re: Help getting started with unit testing
Posted: Thu Apr 17, 2008 7:10 pm
by Christopher
georgeoc wrote:Take a leaf out of the BDD book, and make sure your methods (i.e. test cases) have names which describe the desired functionality. So, requirements like this:
Code: Select all
Rectangle requirements:
- it should return the correct area for two simple integers
- it should throw an error when width or height are not numeric
Circle requirements:
- it should ... etc.
I wonder whether creating requirements, like you have, is a next step or a big conceptual leap? I have a sneaking suspicion that many programmers have read the testing how-tos that show exactly that -- and then when they actually try TDD with their own code they can't think of requirements/specifications like that and lose interest.
How many TDDers actually started with TDD I wonder? Or how many first saw the simpler benefits of writing unit tests, and through that process learned to identify requirements/specifications?
Re: Help getting started with unit testing
Posted: Thu Apr 17, 2008 7:28 pm
by RobertGonzalez
Thank you all for your input. This is an invaluable learning tool.
Re: Help getting started with unit testing
Posted: Fri Apr 18, 2008 2:41 am
by georgeoc
arborint wrote:How many TDDers actually started with TDD I wonder? Or how many first saw the simpler benefits of writing unit tests, and through that process learned to identify requirements/specifications?
I'm only speaking from personal experience, but I started with TDD and have never used retrospective unit tests at all. It did mean that I spent quite some months agonising about the tests I was writing, exactly as you suggest, until suddenly it all became clear when I realised a few things at once - favour Composition over Inheritance, use the new PHP 5 features (Interfaces and type hints especially), and Mocks Rule OK! It was a slow process, but TDD has absolutely changed the way I write code, 100% for the better.
Re: Help getting started with unit testing
Posted: Fri Apr 18, 2008 3:28 am
by Christopher
I think you might be in the 2% who will spend months agonizing about the tests. Most programmers will give it a few days and go back to what works for them. I also think those 2% are the of programmers who more easily conceptualize OOP responsibilities / requirements / specifications. And they are the ones who write TDD tutorials...
Re: Help getting started with unit testing
Posted: Fri Apr 18, 2008 4:00 am
by onion2k
arborint wrote:And they are the ones who write TDD tutorials...
Which is actually part of the problem. Every TDD tutorial I've ever seen has assumed you think it's a great idea in the first place, they never really discuss
why it's a good idea. Like all relatively complicated shifts in the way you code there's always going to be some resistance from coders who don't really want to change (the 98% who go back...).
Selling the idea of spending the extra time adding unit tests to your code would be very beneficial to a lot of people (and their bosses).
Re: Help getting started with unit testing
Posted: Fri Apr 18, 2008 5:00 am
by Jenk
It's because those 2% realise that no body will listen to others, everyone learns the benefits by themselves. I personally know of no one who has used B/TDD and went "back." Only experience will show it's benefits.
Re: Help getting started with unit testing
Posted: Fri Apr 18, 2008 5:37 am
by Chris Corbyn
Jenk wrote:I personally know of no one who has used B/TDD and went "back." Only experience will show it's benefits.
I do. I don't know anybody who used it properly and went back though

When I first started I was a bit rubbish. I gave up for a month or two and when my next big project started I gave it another go. Although I didn't do a very good job of organising my test code I did see the benefits and got hooked. I think this was the key point -- starting from the start. If you've never TDD'd before, trying to start halfway through a project will just put you off. Mostly because everything that was developed prior to the employment of TDD won't be very test-friendly.
Of course, each project you work on beyond that you get better and better. You get better at:
a) Seeing where you need to have settable dependencies so you can mock them
b) Focusing your tests to one behavioural goal at a time
c) Knowing what NOT to test
d) Writing less fragile tests
e) Setting up the test environment
I think (b) is the most important thing to get the hang of. ~arborint's first example he posted was a good demonstration of where you can trip up. Although this was only for testing a really simple concept, there were tests for two different scenarios/behaviours in the same test method. In a more complex scenario a failing test with multiple behavioural expectations in it would cause confusion. The more methods you have like that the worse if gets.
Writing unit tests very badly often just makes your work load increase since you spend more time maintaining your broken tests than you do maintaining the source code. I can give a classic example of something I fix in other people's tests at work all the time. We have some actual system tests (to supplement our units) which really do connect to a database full of test data. Before each test method runs the DB is wiped and the schema/data is re-imported. A common fragile test scenario I run into with this set up is people making assertions about exact row counts in the test data. Of course, when someone adds their own test data this breaks that test. There are all kinds of things you can do badly which just cause tests to break all the time when the system still works as expected... learning to avoid the situations just comes with practise.