2

I'm mocking an API with Prophecy.

A call on the api object to payments() will return an object that has a get($id) method, which returns another object which has a few methods and properties. One of the properties is ID and I want to test that that's what I expect.

Without mocking anything, using the live API, this might work:

$payment = $api->payments()->get(12345);
$this->assertEquals(12345, $payment->id);

To mock the API I'm setting it up like:

$mock_payment = $this->prophesize('Api\\Resources\\Payment');

$mock_payments = $this->prophesize('Api\\Services\\PaymentsService');
$mock_payments->get(12345)->willReturn($mock_payment->reveal());

$api = $this->prophesize('Api');
$api->payments()->willReturn($mock_payments->reveal());

// Now the test code from above:
$payment = $api->payments()->get(12345);
$this->assertEquals(12345, $payment->id)

However I can't figure out how to give the reveal()-ed mock payment object a public ID property, and how to set that to the passed-in ID (12345)?

Edit: simplification of question.

I have a 3rd party API that I cannot change and do not wish to test. It returns instances of certain classes of object with data available through a mix of public properties and getters.

SUT:

function doSomething($api) {
  $result = $api->getResult();
  return "Born: $result->born, " . $result->getAge() . " years old.";
}

I want to test:

function testDoSomething() {
   // ...mock the $api so that getResult() returns an object like the 
   // expected one which has a born property set to "1 April 2016" and a 
   // and a getAge method that will return "1".
   // ...
   $result = doSomething($api);
   $this->assertEquals("Born: 1 April 2016, 1 years old.");
}
artfulrobot
  • 20,637
  • 11
  • 55
  • 81
  • 1
    Serious counter question: What exactly are you testing? You have 3 mocks interacting with each other and then something one mock should have happened. As far as I can tell you will never call actual code to be tested? If you want to test whether the API actually manipulates stuff, you should probably either create a ServiceStub for the API-connection, that returns some response based on a request or make a request against a sandbox of the api. – dbrumann Apr 21 '17 at 15:05
  • @dbrumann thanks for your consideration. I have business logic that uses a 3rd party API via fluent chained calls as in the `$payment = $api...` line above. I don't care to test the API, but I do need the mocked API to return an object with properties of known values. The assertion is just for the sake of keeping the question short and is not the goal of the test. If the api had a single, unchained, method to get a result it would be simpler. I'll try to clarify the question. Many thanks. – artfulrobot Apr 21 '17 at 15:23

1 Answers1

2

I beleive you've forgotten to reveal the api prophecy.

        $mock_payment = $this->prophesize (Payment::class);

        // ---- HERE -----------------
        $mock_payment->id = 12345;
        // ---------------------------
        $mock_payment->get()->willReturn (1);

        $mock_payments = $this->prophesize (PaymentsService::class);
        $mock_payments->get (12345)->willReturn ($mock_payment->reveal ());

        $api = $this->prophesize (Api::class);
        $api->payments ()->willReturn ($mock_payments->reveal ());

        // ---- AND HERE -------------
        $api = $api->reveal();
        // ---------------------------

        $payment = $api->payments ()->get (12345);
        $this->assertEquals (12345, $payment->id);
akond
  • 15,865
  • 4
  • 35
  • 55