PHPUnit contains an assertEquals() method, but it also has an assertSame() one. At first glance it looks like they do the same thing.
What is the difference between the two? Why are they both specified?
I use both sporadically, but according to the docs:
assertSame
Reports an error identified by $message if the two variables $expected and $actual do not have the same type and value."
And as you can see in the example below the above excerpt, they are passing '2204' and 2204, which will fail using assertSame because one is a string and one is an int, basically:
'2204' !== 2204
assertSame('2204', 2204) // this test fails
assertEquals
"Reports an error identified by $message if the two variables $expected and $actual are not equal."
assertEquals does not appear to take datatype into consideration so using the above example of 2204:
'2204' == 2204
assertEquals('2204', 2204) // this test passes
I just ran some unit tests against the above examples, and indeed they resulted in documented behavior.
When it comes to objects comparison:
assertSame
Can only assert if two objects are referencing the same object instance. So even if two separate objects have for all of their attributes exactly the same values, assertSame() will fail if they don't reference the same instance.
$expected = new \stdClass();
$expected->foo = 'foo';
$expected->bar = 'bar';
$actual = new \stdClass();
$actual->foo = 'foo';
$actual->bar = 'bar';
$this->assertSame($expected, $actual); // FAILS
assertEquals
Can assert if two separate objects match their attribute values in any case. So it's the method suitable for asserting object match.
$this->assertEquals($expected, $actual); // PASSES
Reference
$this->assertEquals(3, true);
$this->assertSame(3, true);
The first one will pass!
The second one will fail.
That is the difference.
I think you should always use assertSame.
As it's been said before, assertSame reports an error if the two elements do not share type and value but it's also important to note this from the documentation:
Reports an error identified by $message if the two variables $expected
and $actual do not reference the same object.
So this test would fail too even though they share type and value:
class SameTest extends TestCase
{
public function testFailure()
{
$this->assertSame(new stdClass, new stdClass);
}
}
Moreover,
// Passes
$this->assertSame("123.", "123.");
$this->assertEquals("123.", "123");
// Fails
$this->assertSame("123.", "123");
Never use assertEquals (and family)*
* unless arguments were first manually type-checked, or you're fully aware of the behavior and are prepared for tests to pass when it treats, say, true and 42, as equal--I can't imagine why one would tolerate the risk of extremely subtle false positives in critical testing code.
For primitive values (other than floats) and arrays
Use assertSame. The behavior is predictable. "42" and true are not equal.
For floats
Avoid if possible by using integers. If you must compare floats, assertEqualsWithDelta is as broken as assertEquals:
$this->assertEqualsWithDelta(0.99, "1", 0.1); // passes :\
$this->assertEqualsWithDelta(99999, true, 0.1); // passes D:
The implementation of assertEqualsWithDelta uses an inaccurate approach, as Comparing Floating Point Numbers describes: abs($this->value - $other) < PHP_FLOAT_EPSILON;, so you might want to write a custom function.
Or type-check before calling assertEqualsWithDelta to avoid the more immediate issues:
function is_number($v) {
return is_float($v) || is_int($v);
}
class FloatTest extends TestCase {
private function assertClose(
$expected,
$actual,
$delta = 1e-7
): void {
if (!is_number($expected)) {
$type = gettype($expected);
throw new Error("\$expected type $type was not a number");
}
else if (!is_number($actual)) {
$type = gettype($actual);
throw new Error("\$actual value $type was not a number");
}
else if (!is_number($delta)) {
$type = gettype($delta);
throw new Error("\$delta value $type was not a number");
}
$this->assertEqualsWithDelta($actual, $expected, $delta);
}
public function testFloats() {
$this->assertClose(2, "2"); // fails as it should
}
}
For objects
Use a custom equals(self $other): bool function and assertObjectEquals.
If all else fails
You can't go wrong with assertTrue, the downsides being a lack of semantic appropriateness and poor default error message.
assertSame() == Tests that if the actual output and the expected parameter are same.
that is :
$this->assertSame('$expected','$expected');
or
$this->assertSame('100','100');
assertEquals == If we see with respect to a website page, i have a page which has 2 'table' so when i run assertEquals i will check its count that the 'table' are 2 by using a count function.
Eg:
$this->assertEquals(2, $var->filter('table')->count());
Here we can see that assertEquals checks that there are 2 tables found on the web page. we can also use divisions found on the page using '#division name' inside the bracket.
Eg 2:
public function testAdd()
{
$calc = new Calculator();
$result = $calc->add(30, 12);
// assert that our calculator added the numbers correctly!
$this->assertEquals(42, $result);
}
As previously mentioned, assertEquals() is primarily about an interpreted value, be it by type juggling or an object with an __magic presentation method (__toString() for example).
A good use case for assertSame() is testing a singleton factory.
class CacheFactoryTest extends TestCase
{
public function testThatCacheFactoryReturnsSingletons()
{
$this->assertSame(CacheFactory::create(), CacheFactory::create());
}
}
Related
I have a wrapper that gives me a more convenient API to create mocks. You can ignore the implementation details. Just note the fact that:
it's a trait used on my test classes
the method used to obtain the mock is type-hinted
trait MockFacilitator {
protected function positiveStub (string $target, array $overrides, array $constructorArguments = []):MockBuilder {
$builder = $this->getBuilder($target, $constructorArguments, $overrides);
$this->stubSingle($overrides, $builder);
return $builder;
}
private function stubSingle (array $overrides, MockBuilder $builder):void {
foreach ($overrides as $method => $newValue)
$builder->expects($this->any())
->method($method)->will($this->returnValue($newValue));
}
private function getBuilder (string $target, array $constructorArguments, array $methodsToRetain):MockBuilder {
$builder = $this->getMockBuilder($target);
if (!empty($constructorArguments))
$builder->setConstructorArgs($constructorArguments);
if (!empty($methodsToRetain))
$builder->setMethods(array_keys($methodsToRetain));
return $builder->getMock();
}
Used in my tests like so
$handler = $this->positiveStub(LoginRequestHandler::class, ["isValidRequest" => true]);
Now, the generated stub not only creates the new class that I want (LoginRequestHandler) , but creates new classes for the method return types as well(MockBuilder) ! As is explained on this thread https://stackoverflow.com/a/39070492, such code will not compile.
Is it that phpunit dislikes type hinting method return types? I have seen on other threads that arguments with reference types will equally be overwritten; although in their case, the type was removed altogether, citing inability of phpunit to load those types as the reason. In my case, it probably mocks those return types as well
Is there a way to restrict type generation to only what goes into the mock builder? I prefer to leave out everything else as is in my original class
MockBuilder::disableAutoReturnValueGeneration() can be used to disable the automatic return value generation. The requires using the Mock Builder API instead of createStub(), createMock(), etc., of course.
Of course, automatic return value generation is not triggered, even when it is enabled, which is the default, when you explicitly configure return values for your stubs.
I have a unit test that I am writing and have run into an annoying problem... Let's say I have the following function I am testing:
public function functionToTest(array &$data, parameter2)
{
// perform some action on the array that is being passed in by reference
}
Now, when I attempt to call this function in my unit test, I would do something like this:
public function testMyFunction()
{
$data = array('key1' => 'val1');
$mockOfClass = $this->getMockBuilder('ClassName')
->disableOriginalConstructor()
->setMethods(array('method1', 'method2')) // My function to test is NOT in this list
->getMock();
$this->mockOfClass->functionToTest($data, true);
// Perform assertions here
}
However, I receive the following error message:
Parameter 1 to ClassName::addNewFriendsToProfile() expected to be a reference, value given
This seemed very strange to me. First of all, I'm just passing an array by reference, so it it shouldn't have a problem with this. Secondly, Why parameter 1? Doesn't it mean parameter 0? Then, I tried changing the call to the following:
$this->mockOfClass->functionToTest(&$data, true);
After making this change, it works fine. Unfortunately, it also produces the following warning:
Call-time pass-by-reference has been deprecated in /PathToFile on line xxx
I do not encounter this error when running the actual code. It only throws this error in the unit test. Also, I need to use the mock as there are methods in the class I am mocking; So I can't simply create a new instance of the class and call the method that is being tested. Is there any way I can get around this?
It turns out that PHPUnit clones each of the parameters that are being passed in (Thanks Tim Lytle for pointing me to this source: Pass by reference in a callback when mocking in PHPUnit). This is what causes the error if the array is passed in without a reference at call-time in the unit test. Luckily, the solution is simple. Instead of passing the array by reference, I pass in the array by value and return the array.
before:
public function someFunction(array &$myArray)
{
$myArray[] = 'new val';
}
after:
public function someFunction(array $myArray)
{
$myArray[] = 'new val';
return $myArray;
}
I'm noticing when I use mock objects, PHPUnit will correctly report the number of tests executed but incorrectly report the number of assertions I'm making. In fact, every time I mock it counts as another assertion. A test file with 6 tests, 7 assert statements and each test mocking once reported 6 tests, 13 assertions.
Here's the test file with all but one test removed (for illustration here), plus I introduced another test which doesn't stub to track down this problem. PHPUnit reports 2 tests, 3 assertions. I remove the dummy: 1 test, 2 assertions.
require_once '..\src\AntProxy.php';
class AntProxyTest extends PHPUnit_Framework_TestCase {
const sample_client_id = '495d179b94879240799f69e9fc868234';
const timezone = 'Australia/Sydney';
const stubbed_ant = "stubbed ant";
const date_format = "Y";
public function testBlankCategoryIfNoCacheExists() {
$cat = '';
$cache_filename = $cat.'.xml';
if (file_exists($cache_filename))
unlink($cache_filename);
$stub = $this->stub_Freshant($cat);
$expected_output = self::stubbed_ant;
$actual_output = $stub->getant();
$this->assertEquals($expected_output, $actual_output);
}
public function testDummyWithoutStubbing() {
$nostub = new AntProxy(self::sample_client_id, '', self::timezone, self::date_format);
$this->assertTrue(true);
}
private function stub_FreshAnt($cat) {
$stub = $this->getMockBuilder('AntProxy')
->setMethods(array('getFreshAnt'))
->setConstructorArgs(array(self::sample_client_id, $cat, self::timezone, self::date_format))
->getMock();
$stub->expects($this->any())
->method('getFreshAnt')
->will($this->returnValue(self::stubbed_ant));
return $stub;
}
}
It's like an assert has been left in one of the framework's mocking methods. Is there a way to show every (passing) assertion being made?
After each test method completes, PHPUnit verifies the mock expectations setup during the test. PHPUnit_Framework_TestCase::verifyMockObjects() increments the number of assertions for each mock object created. You could override the method to undo this if you really want by storing the current number of assertions, calling the parent method, and subtracting the difference.
protected function verifyMockObjects()
{
$count = $this->getNumAssertions();
parent::verifyMockObjects();
$this->addToAssertionCount($count - $this->getNumAssertions());
}
Of course, verifyMockObjects() will throw an assertion failure exception if any expectation is unsatisfied, so you'll need to catch the exception and rethrow it after resetting the count. I'll leave that to you. :)
I am a Java programmer and need to work on a Flex/ActionScript project right now. I got an example of using ITreeDataDesriptor from Flex 3 Cookbook, but there is one line of actionscript code that's hard for me to understand. I appreciate if someone could explain this a little further.
public function getData(node:Object, model:Object=null):Object
{
if (node is Office) {
return {children:{label:node.name, label:node.address}};
}
}
The part that I didn't understand was "{children:{label:node.name, label:node.address}}". Office is simply a value object that contains two String properties: name and address.
The following return expression (modified from the question) ...
return {children:{label:node.name, body:node.address}}
... is functionally equivalent to this code ...
var obj:Object = new Object();
obj.children = new Object();
obj.children.label = node.name;
obj.children.body = node.address;
return obj;
The anonymous object returned in the question code complicates matters because it defines a property twice. In that case, the first declaration is used, and the subsequent one(s) are ignored. No compile-time or runtime error is thrown.
I think in Java you would call that a map or an associative array. In Javascript and Actionscript you can say this to create an object with certain properties:
var myobject = {
'prop1': 100,
'prop2': {
'a': 1
}
}
trace( myobject.prop1 ); // 100
trace( myobject.prop2.a ); // 1
In your example it's just returned as a nameless object.
return {children:{label:node.name, label:node.address}};
Means you are returning a new Object. The {} are the Object's constructor, and in this case its an Anonymous object.
Thank you both for the quick response. So if I understand your explanations correctly, the return statement is returning an anonymous object, and this object has only one property named "children", which is again an associative array - ok, here is the part I don't quite understand still, it seems that both properties in this array are named "label", is this allowed?
What is the syntax to declare a type for my compare-function generator in code like the following?
var colName:String = ""; // actually assigned in a loop
gc.sortCompareFunction = function() : ??WHAT_GOES_HERE??
{
var tmp:String = colName;
return function(a:Object,b:Object):int { return compareGeneral(a,b,tmp); };
}();
Isn't "Function" a data type?
In order to understand what the data type is, we must know what the intended outcome of the return is. I need to see the code block for compareGeneral, and I still don't believe this will help. You have two returns withing the same function "gc.sortCompareFunction", I believe this is incorrect as return gets a value and then acts as a break command meaning the rest of the anything withing the same function block is ignored. The problem is that I don't know which return is the intended return, and I don't know that flash knows either. You can use * as a data type, but this should only really be used in specific situations. In this situation I believe you need only the one return value that merely returns whatever the value of compareGeneral.
Now if this is a compareGenerator it really should either return a Boolean TRUE or FALSE, or a int 0 or 1, lets use the former. Also I believe we can use one less function. Since I have not seen all of your code and I am not exactly sure what your trying to accomplish, the following is hypothetical.
function compareGeneral(a:object,b:object):Boolean
{
//Check some property associated to each object for likeness.
if(a.someAssignedPropery == b.someAssignedPropery)
{
return true;
}
return false;
}
var objA:Object = new Object();
objA.someAssignedProperty = "AS3";
objB.someAssignedProperty = "AS3";
compareGeneral(objA,objB);
In this case compareGeneral(objA,objB); returns true, though we haven't done anything useful with it yet. Here is a way you may use it. Remember that it either returns a value of true or false so we can treat it like a variable.
if(compareGeneral(objA,objB)) //same as if(compareGeneral(objA,objB)) == true)
{
trace("You have found a match!");
//Here you can call some other function or set a variable or whatever you require functionality wise based on a match being found.
}
else
{
trace("No match could be found!");
}
I hope that this is able to help you understand data types and return values. I do not know what you were doing with tmp, but generally functions that return a value deal with that one thing and only that thing, so it is best that the compare function compare one thing against the other and that be the extent of the call. Whatever functionality you require with tmp can go inside its own function or method, and be called when needed.