4

Let's take in account this scenario

public class FooManager()
{
    //somewhere in my manager class code
    public function merge(Foo $a, Foo $b, $id)
    {
        if ($a->getId() == $id) { //!! PAY ATTENTION !!
        }
        //and so on
    }
}

I've read pretty much everywhere that mock an entity isn't a good practice but I'm wondering about that particular piece of code.

If I can't assign an Id to a real entity object (as, for example, it is auto-generated by doctrine), how can I test this functionality without mocking the Foo entity?

Update

I was thinking about this question and something "flash" in my mind: I'm testing FooManager here, not Entities. So, to me, use mock is not the only solution but, maybe, even the best. Someone could help me understand if my thinking process is a good one or not?

Update 2

I didn't mentioned it before but, of course, I need to test changes into $a and $b ($a will receive some properties values from $b and will update its properties accordingly). This is the goal of the test as goal of FooManager is to merge $b properties into $a ones (applying some logic, of course)

Community
  • 1
  • 1
DonCallisto
  • 29,419
  • 9
  • 72
  • 100

3 Answers3

0

If you need to bypass the auto-generated entity id strategy of doctrine, you can act via metadata, and set a know id. As example, if you load your test fixtures with the DoctrineFixtureBundle, you can have, in your fixtures:

class FooFixtures  extends AbstractFixture implements OrderedFixtureInterface
{
    public function load(ObjectManager $manager)
    {

        $metadata = $manager->getClassMetaData("Acme\\DemoBunde\\Entity\\Foo");
        $metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE);
...        
        $foo = new Foo();
        $foo ->setId(3);
        $foo ->setTitle('Foo example');

And then use it in your testing class

public function setUp()
{
    parent::setUp();

    $this->fooFixtures = new FooFixtures();

    $this->loadFixtures(array($this->fooFixtures));
 }

So now you have in your database a Foo instance with id equal to 3

Hope this help and that i understand your needed

DISCLAMER: Is not a good practice to hardcode your test with database id!!!!

Matteo
  • 37,680
  • 11
  • 100
  • 115
  • This is clear but is not a solution at all as I have to be able to test that procedure even without db data. Moreover, if I use the "real" entity, I can't use prophecy methods to test values and this point me into direction that, maybe, mock (or stub) is the only solution there (even if I never call getId() into my manager) – DonCallisto Sep 10 '15 at 15:30
  • 1
    Moreover I can extend my class (that is simple) directly into *Spec class, call it TestFoo() and implement setId() but, as I said previously, I'm afraid that this isn't a solution at all and mock the object is the only one that makes sense – DonCallisto Sep 10 '15 at 15:36
0

What I reach.

My first idea, as I said previously, was to create a mock just because phpspec + doctrine seems to force me to.

After a while, however, I came through a messy ->getFoo()->hasToBeCalled(), ->setFoo()->hasToBeCalled() sequence where all the properties "touched" by FooManager needed an expectation. This force me to re-think about this solution and no, to me isn't the best one.

I've inspected my class and the fact that SUT isn't directly what I want to test here, but what I need to check is the effect of this action onto an entity (is anyway a behaviour, right?), I've decided to proceed as follows:

  • Create a TestFoo entity (inside spec\folderToMySpec\FooSpec.php)

    class TestFoo extends Foo
    {
        public function setId($id) 
        {
            $this->id = $id
        }
    }
    

    that way I'll both be able to set $id directly (is not a behaviour that I need to test as doctrine, under normal circumstances will do it for me) and to keep this class outside my /src codebase

  • I've modified my merge() function to return only the entity I need to check for testing purposes (and, not, I don't make it only for test's sake as could be useful to have some informations about the entity that was merged)

  • Finally, with this returned entity directly from SUT, I can use Prophecy matchers as I would normally with SUT itself.

This solution has many advantages as I don't need to write every single method that will be called by mocked entities and let me feel free to test with returned stub only what I really need to test.

Any thoughts?

DonCallisto
  • 29,419
  • 9
  • 72
  • 100
-1

If you need mock for write test it's normal to create it.

In this concrete case you need a stub. I recommend read this cute article for explain difference between mock & stub.

Robert C. Martin says: Mock across architecturally significant boundaries, but not within those boundaries. You can start from this point for research.

And my opinion: since in this case you need create a mock for write correct test — create it. I don't see negative side of this action.

Onedev_Link
  • 1,981
  • 13
  • 26
  • Isn't a stub a "particular" kind of Mock? Reading on the internet ... I think so. Moreover, that doesn't help me, am I wrong? – DonCallisto Sep 10 '15 at 14:58
  • In that article Robert C. Martin explain difference between Dummy, Stub, Spy, Mock, and Fake. Anyway, tests is primary. If you can't assign id to real entity object you should create stub, or mock for testing purpose. – Onedev_Link Sep 10 '15 at 15:01
  • Thanks, i refactored my answer. – Onedev_Link Sep 11 '15 at 08:54