1

I have a service MoneyMover that change state of two entities: reduce amount of money from sender and adds this amount to receiver.

I don't have implementation yet. And according to BDD I'm writing spec for this feature:

function it_should_reduce_balance_of_sender(User $sender, User $receiver)
{
    $sender->getBalance()->willReturn(5);
    $this->moveFromSenderToReciever($sender, $receiver, 5);
    $sender->getBalance()->shouldReturn(0);
}

But I can only test state of my service not of any prophecy objects that are used. And so this spec throw a fatal error:

Call to undefined method Prophecy\Prophecy\MethodProphecy::shouldReturn()

How can I test state change of an entity?

I know that I can use something like that:

$sender->getBalance()->shouldBeCalled()->willReturn(5);
$sender->setBalance(0)->shouldBeCalled();

$this->moveFromSenderToReceiver($sender, $reciever, 5);

But what if I just want to test that state has been changed?

Michael Sivolobov
  • 12,388
  • 3
  • 43
  • 64

2 Answers2

0

Your aversion towards verifying it that way doesn't seem too unreasonable. But rather than trying to bend phpspec to your will, perhaps a cleaner way of solving this is to avoid setters & getters altogether. What about something like this?

$sender->decreaseBalanceBy(5)->shouldBeCalled();

$this->moveFromSenderToReceiver($sender, $reciever, 5);

This way it reads more naturally, and you don't need to worry about managing the state of the users balance from the outside.

nj_
  • 2,219
  • 1
  • 10
  • 12
0

You are ok with

$sender->getBalance()->shouldBeCalled()->willReturn(5);
$sender->setBalance(0)->shouldBeCalled();

$this->moveFromSenderToReceiver($sender, $reciever, 5);

that's the correct approach as $sender and $receiver are two collaborators.

You need to stop and think: what my class does? if your answer is changing the balance of particular user, well I'm afraid that's not the correct answer(*).
A better one could be: the behavior of your class is to "send a message" or "pass the responsibility" to user saying that he needs to set a difference balance. You are perfectly safe here saying, ok, if I invoke that method on my class, setBalance and getBalance should be called (because that's what your class is supposed to do!).

If you want to check that a balance value is setted (or returned) correctly, you must create another spec class (that will spec your User class) and perform all tests that will put you in a confident state with your code.

That way, if both test are green, then you can safely rely on what you have written.

Remember that PHPSpec is a BDD tool, not a TDD one, so you have to keep your focus on those kind of things: "what's the behavior of my class?" is THE question.

Just for completeness, think about external code that you don't own, such ORM o DBAL or whatever you could use to perform I/O operation with a database. Say that class you are testing describing needs to write values onto database: you will never check that actual data are written. What you will do, instead, is to check if methods calls (such write, read or whatever name they got) are invoked: if they are, you are ok with it as, hopefully, your library got its own test suites that perform checks on whatever is the interaction between their own code and database.

(*) That's not the correct answer as you're not directly call $user->amount = 0. First, that's silly as amount, hopefully, is a private member of User class. So you're just passing the responsibility to someone else (User class, where the logic should belong). Of course, at a higher level of abstraction, saying that your class changes the balance of a particular user is ok, here I needed this kind of separation just to focus reader's attention onto a different point of view that could really, really help to understand why PHPSpec is so restrictive for this kind of operation, if you make a comparison with other tools, like PHPUnit or whatever.

DonCallisto
  • 29,419
  • 9
  • 72
  • 100