Radek Simko
Radek Simko

Reputation: 16146

How to test multiple properties of an object

I'm getting a JSON structure from an API and need to check, whether the successfull response has two specific attributes with specific values.

Key problems:

  1. I cannot compare the whole object, as there are some properties, which may vary with each request
  2. I cannot write two tests (for each attribute), because it can be considered as successful response only when both attributes matches the right values.

Example successful response:

{
    'success': true,
    'user_ip': '212.20.30.40',
    'id': '7629428643'
}

Dirty solution would be

<?php
public function testAddAccount() {
    $response = $this->api->addAccount( '7629428643' );

    $this->assertTrue(
        $response->success === TRUE &&
        $response->id === '7629428643'
    );
}

But I think there must be better and cleaner solution, is there?

Upvotes: 8

Views: 2729

Answers (6)

Bugs
Bugs

Reputation: 1452

Compute the difference between the response and the right answer ignoring any excessive values. If there's no difference, everything's fine; if there is, you'll get the complete information.

//some examples
$responseValues = array('success' => true, 'user_ip' => '212.20.30.40', 'id' => '7629428643'); //success
$errorResponseValues = array('success' => false, 'user_ip' => '212.20.30.40', 'id' => '7629428643'); //failure
$errorResponseValues2 = array('success' => false, 'user_ip' => '212.20.30.40', 'id' => '123'); //failure

$expectedValues = array('success' => true, 'id' => '7629428643'); //what is success

function whatIsWrong($reality, $expectation)
{
    return array_uintersect_assoc($reality, $expectation, function($a, $b){return (int)($a == $b);}); //This is slightly dirty, I think the final implementation is up to you
}

var_dump(whatIsWrong($responseValues, $expectedValues)); //array()
var_dump(whatIsWrong($errorResponseValues, $expectedValues)); //array('success' => false)
var_dump(whatIsWrong($errorResponseValues2, $expectedValues)); //array('success' => false, id => 123)

Then you may use assertEqual(whatIsWrong(...), array()), which should output the difference on failure, or handle it in pretty much any preferred way.

Upvotes: 1

Bogdan Stăncescu
Bogdan Stăncescu

Reputation: 5460

The cleanest way I can think of (although this is a matter of taste) would be to add the two boolean expressions and assert the result is two:

$r1=$response->success === TRUE;
$r2=$response->id === '7629428643';
$this->assertEquals(2, $r1+$r2, "Expecting both values to match; got ($r1) and ($r2)");

Of course, you can extend this setup to any number of tests with arrays and array_sum()==count(), and more loveable messages.

Upvotes: 0

JamesArmes
JamesArmes

Reputation: 1373

You want to use assertEquals() for each property you want to verify. While you typically want to test only one thing in each test case, sometimes multiple assertions are required in order to test that "one thing". For this reason, multiple assertions are okay.

You may also want to assert that the properties exist on the $response object to avoid notices. See the following for an example:

public function testAddAccount() {
    // Attempt to create an account.
    $response = $this->api->addAccount('7629428643');

    // Verify that the expected properties are present in the response.
    $this->assertObjectHasAttribute('success', $response);
    $this->assertObjectHasAttribute('id', $response);

    // Verify the values of the properties.
    $this->assertEquals(true, $response->success);
    $this->assertEquals('7629428643', $response->id);
}

Upvotes: 2

shybovycha
shybovycha

Reputation: 12265

As mentioned in some answer, you should use assertEquals() method. The second part is that you might use a few assertions. So if the phpunit detects failure on the first assertion, the whole test fails.

Why not to use assertTrue()? Because it is not clear if you, for example, use if ($object) ... in one place of code and assertTrue($object); in other one.

Also, be sure your routine executes correctly. So if you get the exception while trying to get the response - test will fail. While exceptions should be checked also as a test cases. That is, you should try posting an incorrect request and assert exception is thrown. That's how good test looks like - it should check all possible situations, hehe =)

So, this is the code you should got:

public function testAddAccount() {
    $response = $this->api->addAccount('7629428643');

    $this->assertEquals($response->success, true);
    $this->assertEquals($response->id, '7629428643');

    $this->setExpectedException('MyException');

    $response = $this->api->addAccount('wrong_account');
}

Upvotes: 0

David Harkness
David Harkness

Reputation: 36562

You can use multiple assertions in a single test method for cases like this. Also, use the more specific assertions such as assertEquals whenever possible as these will provide better failure messages.

public function testAddAccount() {
    $response = $this->api->addAccount( '7629428643' );

    self::assertTrue($response->success);
    self::assertEquals('7629428643', $response->id);
}

Upvotes: 0

Sem
Sem

Reputation: 4669

If assertTrue() stores the boolean of a succesfull response this is the way how I would handle it. Keep in mind that this is a matter of taste.

private $lastResponse;
// $id given elsewhere
public function testAddAccount($id) {
   $this->lastResponse = $this->addAccount($id);
}

private function addAccount($id) {
   return $this->api->addAccount($id);
}

private function isLastResponseValid($response){
   return $this->lastResponse->success === TRUE 
   && $this->lastResponse->id === '7629428643';
}

Upvotes: 0

Related Questions