And the last 50 minutes have been particularly painful.
I’ve been bringing FOSUserBundle, Dunglas API Bundle, Lexik JWT Bundle, and Behat (amongst a good few others) together into one project for the first time.
Most of today has been productive. I’ve solved two of my big headaches, but as the hours have gone by, tiredness has set in.
And rather than call it a night, I did my usual “I’ll just see if I can…” once too many times, and buggered everything up.
If I had been behaving, I could have quickly done a little
git bisect magic and found the source of my woes. Alas, I had not been behaving.
You have requestedanon-existent service"test.client".
Wot do?
Well, there is a major shortage of Google help on this one.
The two most likely answers were not it.
Behat had been working just fine. I knew it was my mistake. But the project is growing and there’s just a ton of possible files that could have changed. When will I learn?
As it turned out, I had changed my
behat.yml file in my tired stupor:
1
2
3
4
5
6
# behat.yml
extensions:
Behat\Symfony2Extension:
kernel:
env:"test"
debug:"true"
I’d changed
env to a new Symfony Environment I had created so as not to keep messing up my local
dev database every time I re-ran behat.
In my case changing back to
env:"test" solved all my problems.
Ok, well not all of them… plenty of work still to do. But that’s it for this evening.
I hope that saves someone a headache in the future.
I’m a huge fan of PHPSpec, and its close cousin, Behat. I find when writing code in conjunction with PHPSpec, I am able to enter a rhythm that I have never found with any other tool.
I particularly enjoy the code generation functionality – describe some action, do a
bin/phpspec run and have your methods created for you as you go. It really is quite a joy to use.
However, as with any tool, there is a learning curve.
I found the basics – the stuff described in the manual – to be straightforward enough that even when stuck, I could relatively quickly find my way through and get back on track.
Then, recently, I decided to build an application involving third party / social media providers for authentication using HWIOAuthBundle.
Along the way, I added the concept of a
User object having a Collection (
Doctrine\Common\Collections\Collection) of
Account objects. Fairly common stuff, particularly if you have ever used Symfony at all.
This is absolutely a work in progress. I even left in the last test, yet to be started.
As a quick side note, for the first time I have decided to declare commonly required objects by way of the
let() method. I am not absolutely sure whether or not this is good practice, but it does seem to work as I intended it too. If you know differently, do let me know by way of leaving a comment – thanks !
Currently the tests are passing – with the exception of the pending example still to write.
What may be less obvious is how much effort I went through to get the
it_can_connect_one_account_to_a_social_media_service() example to play ball.
I’ve spent a good few hours these past few evenings trying to figure out this error:
1
2
3
4
AppBundle/OAuth/Connect/ProfileConnector
51-it can connect one account toasocial media service
warning:array_key_exists():The first argument should be eitherastringoran integerin
A bit of Google-foo told me I was likely going about this entirely the wrong way:
When the creator of PHPSpec tells you (indirectly) that you are wrong, then… you are wrong.
I could actually see the problem – the implementation seemed correct, but PHPSpec sees my call
$profile->getId() as returning an Object, and then throwing an error something along the lines of :
1
2
3
4
AppBundle/OAuth/Connect/ProfileConnector
51-it can connect one account toasocial media service
error:Objectof classProphecy\Prophecy\MethodProphecy could notbe converted tostringin
At first I figured… ok, well I won’t mock the
User object – I can new that up – but when trying to add a mock
Profile to the real collection, it all went a bit wrong:
1
2
3
4
5
6
AppBundle/OAuth/Connect/ProfileConnector
50-it can connect one account toasocial media service
That’s fine, I thought, I will use a real
Profile object as well, because why not?
Well, I’ll tell you why not.
The test expects my
Profile object to have an
id of 16. I’m not about to add in a
setId() method, that would be bonkers.
At heart, I knew as soon as I started adding in real objects that I was heading down the wrong path.
Generally I find that when PHPSpec is making your life hard it is because you are trying to do the wrong thing. Sooner or later you must stop resisting.
Picard lost most of his hair due to frustrating late night bug fixing in ten forward. It’s all explained in the Season 3 episode Bynar2Hex.
Anyway, after quite a bit a lot of further hackery, I managed to find a working solution (as per the earlier sample):
When I think about it now, it does make sense. This is similar to what I was trying to do, only this way conforms with the way PHPSpec expects me to behave 🙂
Both the
User and
Profile objects are properly mocked, but as per Everzet’s comment, we are returning a real ArrayCollection, which by way of
$profile->getWrappedObject() will return the underlying
Profile objects, rather than the PHPSpec wrapped objects / Object Prophecies.
This is exactly the sort of problem that my brother – an aspiring coder – would think I “just knew” how to fix. And that he should also be expected to know how to solve instinctively also.
Of course, the next time this comes up, I will know exactly how to solve it. It’s just if you were sat watching over my shoulder, you wouldn’t imagine the hours of my life that lesson took to learn 😉
Behaviour Driven Development (BDD) with Behat 3 is a thing of beauty. When combined with PHPSpec you get something I am hugely excited about.
However, there are many ways in which BDD can be daunting not just for the new-comer, but for a new project in general.
Once you have a feature or two written up, copy / pasting and doing a little editing can yield quick results but writing the code that lives in the underlying Feature Context can be a little harder. For me, this was most evident when dealing with Scenarios that contained tabular data.
I made myself a little Behat TableNode cheat sheet to help – at a glance, and whilst in my IDE (PHPStorm btw) – figure out just what might be in my TableNode objects for the specific methods available on that class.
This is an example of the scenario I was working with:
Behat 3 TableNode var_dump
1
2
3
4
Background:
Given the product with id:3has the following values:
Hopefully this is as useful a reference to you as it has become for me. Being able to quickly ‘guess’ what is going to be in my TableNode objects and where has helped save me a good deal of time already.