Help getting started with unit testing

Discussion of testing theory and practice, including methodologies (such as TDD, BDD, DDD, Agile, XP) and software - anything to do with testing goes here. (Formerly "The Testing Side of Development")

Moderator: General Moderators

User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Help getting started with unit testing

Post 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.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: Help getting started with unit testing

Post 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.
(#10850)
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Re: Help getting started with unit testing

Post 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)?
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: Help getting started with unit testing

Post 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.
(#10850)
User avatar
arjan.top
Forum Contributor
Posts: 305
Joined: Sun Oct 14, 2007 4:36 am
Location: Hoče, Slovenia

Re: Help getting started with unit testing

Post by arjan.top »

maybe it will sound stupid but does 0? NULL? do? Never used it ... :oops:
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: Help getting started with unit testing

Post by Christopher »

arjan.top wrote:maybe it will sound stupid but does 0? NULL? do? Never used it ... :oops:
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?"
(#10850)
User avatar
arjan.top
Forum Contributor
Posts: 305
Joined: Sun Oct 14, 2007 4:36 am
Location: Hoče, Slovenia

Re: Help getting started with unit testing

Post by arjan.top »

Now i feel even more stupid :banghead:

:D
georgeoc
Forum Contributor
Posts: 166
Joined: Wed Aug 09, 2006 4:21 pm
Location: London, UK

Re: Help getting started with unit testing

Post 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...
}
 
 
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: Help getting started with unit testing

Post 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?
(#10850)
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Re: Help getting started with unit testing

Post by RobertGonzalez »

Thank you all for your input. This is an invaluable learning tool.
georgeoc
Forum Contributor
Posts: 166
Joined: Wed Aug 09, 2006 4:21 pm
Location: London, UK

Re: Help getting started with unit testing

Post 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.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: Help getting started with unit testing

Post 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...
(#10850)
User avatar
onion2k
Jedi Mod
Posts: 5263
Joined: Tue Dec 21, 2004 5:03 pm
Location: usrlab.com

Re: Help getting started with unit testing

Post 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).
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Re: Help getting started with unit testing

Post 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.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Help getting started with unit testing

Post 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.
Post Reply