# Testing a temperature converter

I’m sure we all know the formula to convert Celsius to Fahrenheit, or convert Fahrenheit to Celsius.
It goes something like this:

celsius2fahrenheit = (temperature * 9/5) – 32
fahrenheit2celsius = (temperature – 32) * 5/9

I always forget and have to experiment a bit before I get the pluses, fives, and nines all in the right places — not to mention the correct spelling of f-a-h-r-e-n-h-e-i-t.

Luckily, I know 4 temperatures that I can test on scratch paper with this formula.  They are:

1. The boiling point of water (212F or 100C)
2. The freezing point of water (32F or 0C)
3. Standard temperature (59F or 17C)
4. Forty below (-40F and -40C)

Today, I decided to codify it in a PHP script when I saw this request on PeoplePerHour.com

Now, I like to think I’m above doing someone’s homework for them (just barely), but I thought I’d take up the challenge.   I also decided I would use PHPUnit to write the tests and came up with this:

TempConverter.php

```<?php

class TempConverter {
public static function c2f(\$temp) {
return \$temp * 1.0 * 9/5 + 32;
}
public static function f2c(\$temp) {
return (\$temp -32) * 1.0 * 5/9;
}
}```

TestTempConverter.php

```<?php

require_once('TempConverter.php');

class TempConverterTest extends PHPUnit_Framework_Testcase
{
public function testInstantiation() {
\$obj = new TempConverter();
\$this->assertTrue(\$obj instanceof TempConverter);
}

public function testStandardTemp() {
\$standardF = 59;
\$standardC = 15;

\$this->assertEquals(\$F, TempConverter::c2f(\$standardC));
\$this->assertEquals(\$C, TempConverter::f2c(\$standardF));
}

public function testFreezing() {
\$freezingF = 32;
\$freezingC = 0;

\$this->assertEquals(\$freezingF, TempConverter::c2f(\$freezingC));
\$this->assertEquals(\$freezingC, TempConverter::f2c(\$freezingF));
}

public function testBoiling() {
\$boilingF = 212;
\$boilingC = 100;

\$this->assertEquals(\$boilingF, TempConverter::c2f(\$boilingC));
\$this->assertEquals(\$boilingC, TempConverter::f2c(\$boilingF));
}

public function testFortyBelow() {
\$fortyBelow = -40;

\$this->assertEquals(\$fortyBelow, TempConverter::c2f(\$fortyBelow));
\$this->assertEquals(\$fortyBelow, TempConverter::f2c(\$fortyBelow));
}
}```

After a bit of shuffling I figured out the righrt formulas, and got all my tests passing.

As luck would have it, I had pretty good coverage with my test data. Not just a wide range of temperatures, but a wide variety of inputs as well. I’ve got positive numbers, negative numbers, zero, and even a result with identical numbers. But what could I do to improve?

My first thought was that I knew one other number for comparison, absolute zero, the temperature at which atoms stop moving: zero Kelvin or -273C. A quick google search gave the Fahrenheit number -459.67F So I added this test case:

```	public function testAbsoluteZero() {
\$absoluteZeroF = -459.67;
\$absoluteZeroC = -273;

\$this->assertEquals(\$absoluteZeroF, TempConverter::c2f(\$absoluteZeroC));
\$this->assertEquals(\$absoluteZeroC, TempConverter::c2f(\$absoluteZeroF));
}```

When I ran again, I got this message:

```There was 1 failure:

1) TempConverterTest::testAbsoluteZero
Failed asserting that <double:-459.4> matches expected <double:-459.67>.```

After a little head scratching (and a bit more googling) I learned that they’ve changed the bar, and 0 Kelvin is now precisely -273.15C. I fixed the test and ran it again, only to get this puzzling answer:

```There was 1 failure:

1) TempConverterTest::testAbsoluteZero
Failed asserting that <double:-459.67> matches expected <double:-459.67>.```

Now that was some odd behavior. Obviously my code was working, and I was getting the right result, but something was amiss. I tried this, which passed:

`		\$this->assertEquals(\$absoluteZeroF, round(TempConverter::c2f(\$absoluteZeroC), 2));`

Clearly it was either a rounding in PHP or something wrong with float comparisons in PHPUnit.

But that’s not what I wanted to talk about.  I wanted to discuss how to structure tests. Let’s comment that out for now.

A common way of writing unit tests is to test each method in the system under test with one test method.  I could have written something like this:

```	public function testC2F() {
\$standardF = 59;
\$standardC = 15;
\$freezingF = 32;
\$freezingC = 0;
\$boilingF = 212;
\$boilingC = 100;
\$fortyBelow = -40;
\$absoluteZeroF = -459.67;
\$absoluteZeroC = -273.15;

\$this->assertEquals(\$standardF, TempConverter::c2f(\$standardC));
\$this->assertEquals(\$freezingF, TempConverter::c2f(\$freezingC));
\$this->assertEquals(\$boilingF, TempConverter::c2f(\$boilingC));
\$this->assertEquals(\$fortyBelow, TempConverter::c2f(\$fortyBelow));
}

public function testF2C() {
\$standardF = 59;
\$standardC = 15;
\$freezingF = 32;
\$freezingC = 0;
\$boilingF = 212;
\$boilingC = 100;
\$fortyBelow = -40;
\$absoluteZeroF = -459.67;
\$absoluteZeroC = -273.15;

\$this->assertEquals(\$standardC, TempConverter::f2c(\$standardF));
\$this->assertEquals(\$freezingC, TempConverter::f2c(\$freezingF));
\$this->assertEquals(\$boilingC, TempConverter::f2c(\$boilingF));
\$this->assertEquals(\$fortyBelow, TempConverter::f2c(\$fortyBelow));
}
```

This works reasonably well besides the duplication of test data (which could be solved with class constants), but the point I’m trying to make is that you can test a scenario better the other way. Then your test function describes your test scenario.

It could easily be refactored into a data driven test with a comment denoting the scenario, and an allowance for rounding errors:

```	/**
* @dataProvider knownTemperatures
*/
public function testDataDrivenConversion(\$f, \$c, \$scenario) {
\$digits = 7;

\$this->assertEquals(round(\$c, \$digits), round(TempConverter::f2c(\$f), \$digits), \$scenario);
\$this->assertEquals(round(\$f, \$digits), round(TempConverter::c2f(\$c), \$digits), \$scenario);
}

public function knownTemperatures() {
\$tempuratures = array(
array(59, 15, 'compare standard temperatures at standard pressure'),
array(32, 0, 'compare the freezing point of water'),
array(212, 100, 'compare the boiling point of water'),
array(-40, -40, 'compare forty below zero (should be the same for both)'),
array(-459.67, -273.15, 'compare absolute zero'),
array(6, -14.44444444, 'compare a random number'),
);

return \$tempuratures;
}
```

The source code is available at:

http://one-shore.com/aaron/TempConverter.zip