-
Notifications
You must be signed in to change notification settings - Fork 6
The difference between Mockery and Phony #199
Comments
Good question, and one that kind of cuts to the heart of the "philosophy" difference between the two frameworks. I'll write a proper response soon, but I'll at least give you some examples of what I would do. I would test the non-error case like so: public function testStrictWithPhony()
{
$fooHandle = Phony::mock(FooInterface::class);
$fooHandle->bar->returns('value');
$strict = new Strict();
$this->assertSame('value', $strict->call($fooHandle->get()));
} And the error case like this: public function testStrictExceptionWithPhony()
{
$foo = Phony::mock(FooInterface::class)->get();
$strict = new Strict();
$this->setExpectedException(RuntimeException::class);
$strict->call($foo);
} This probably raises more questions. Sorry about that, I'm just about to head home for the week. In the mean time, there's a pretty fundamental difference between Mockery's approach and Phony's. Mockery uses what is sometimes called the "expect-run-verify" pattern, which some mocking frameworks, like Phake, Prophecy, and Phony deliberately avoid. You can read about the original coining of the term at expect-run-verify… Goodbye!. This is by the person who wrote Mockito, a massively popular Java mocking library, that introduced a lot of the ideas that influenced Phony. There's also some useful info on the Mockito Wikipedia page. Hope that helps for now. |
@ezzatron I think I get the gist of what you mean but I'll still really appriciate the proper response. :-) |
So, it seems the first time I read this question, I missed the "without getting rid of any assertions" part of the question. That makes it a much more difficult question to answer. One of the big differences between Mockery and Phony is that Mockery uses "expectations", whereas Phony uses "stubbing" and "verification". Expectations are essentially stubbing and verification mashed together, so there's a lot of overlap in their features. Expectations force you to define every interaction that will happen before the actual body of test takes place. In contrast, Phony splits this into rules about how to respond when called (stubbing), and ways to assert what did happen once the body of the test is complete (verification). Personally, my first experience with mocking was PHPUnit's mocks, which use expectations. After writing a lot of tests, I eventually realized that PHPUnit's mocks had some big problems:
Once I realized this, I started looking for alternatives, and found other mocking frameworks, which completely abandoned expectations in lieu of stubbing and verification. It took quite a while to really get it, but getting rid of expectations solved pretty much all of the above problems for me. Unfortunately I don't have enough experience with Mockery to say with confidence whether it manages to avoid these issues better than PHPUnit's mocks did. Regardless, along the way, I learned other ways to make my tests less "brittle". There's so many aspects to the concept of test "brittleness", but one thing that always seems to help, is to reduce the amount of assertions I'm making to the bare minimum. Mocking frameworks that provide stubbing and verification fit well into this approach, because they give you the power to make decisions about what assertions you're making in each test. If some aspect of a system's behavior is irrelevant to your current test, you don't have to make additional expectations just to get the test working. You also get the benefit of being able to push some of your stubbing off into the shared "set up" stage of a test suite, without requiring that every test in the suite makes use of it. The trade-off for these benefits is that you have to be more explicit about what you're asserting. Generally speaking, this isn't a bad thing. If you've got a really complicated test, with lots of assertions, it's usually a sign that you're trying to do too much in one test, or that your code is overly complex. At the end of the day, you have to decide which tool fits your needs better. Personally, I'll always avoid expectations because I feel that they cause more problems than they solve. In addition to all of that, I have some more specific comments on your original examples. In both examples you use these two verifications: $fooHandle->bar->once()->called();
$fooHandle->bar->calledWith(); Which could probably be collapsed into one: $fooHandle->bar->once()->calledWith(); Because all Phony verifications support cardinality modifiers like Also note that with Phony, unless you explicitly care about the number of times $fooHandle->bar->calledWith(); This is because Phony has a default cardinality of There are also a couple of non-Phony things I would suggest. Here is a revised version of the error-case, that does not remove any assertions: public function testStrictExceptionWithPhony()
{
$fooHandle = Phony::mock(FooInterface::class);
$fooHandle
->bar
->returns(null);
$strict = new Strict();
$this->expectException(RuntimeException::class);
try {
$strict->call($fooHandle->get());
} catch (RuntimeException $e) {
$fooHandle
->bar
->once()
->calledWith();
throw $e;
}
} There are two things of note here:
Beyond these changes, for these particular cases there's probably nothing you can do make the Phony examples as simple as the Mockery examples unless you make less assertions. But from my point of view at least, the amount of assertions being made here can only lead to very brittle tests, which is something you should seek to avoid in any case. |
Closing to clean up active issues. Please feel free to continue discussion. |
I'm not posting this to complain. It's just that I got the feeling like I'm missing some better way to test things or a point how Phony is intended to be used. I'd really appriciate some feedback what I'm doing wrong and what would you have done otherwise.
Let's have some code to test:
Now let's test the simple case where no exception is thrown. With Mockery it's pretty straightforward:
Then I've tried to rewrite the same test with Phony without getting rid of any assertions and here is what I got:
As you can see it's way longer which raised an exclamation mark in my head that I might be doing something wrong here. I'm unable to tell what though. But let's continue.
Next I need to test the case that raises the exception. Let's start with Mockery again:
Easy enough. Now again the same test with Phony:
... Yep I'm definitely doing something wrong here. Could you point me in the right direction?
The text was updated successfully, but these errors were encountered: