Do you test your application in the right way? Or do you attempt to do it all at once, in many locations? When you're stuck, do you ask for help?
Recently I was a bit stuck, trying to figure out how to test a section of an application I've been developing. Specifically, I was trying to mock a HydratingResultSet in a controller test, so it could be the return value of a method call on a datasource, my controller needed.
I was sure it was the right approach to help ensure the functionality in question was working properly. But no matter what I tried, my tests didn't work, because I wasn't mocking it correctly. So, after some time experimentation, I did what I do increasingly often, jumped in to #zftalk on IRC.
I asked for help, laying out the problem as I saw it. The first response which came back, from Ocramius, stopped me dead in my tracks:
Why are you trying to do that?
Honestly, I'd not stopped to think if it was the right thing to do. I'd simply assumed it was. The moment he gave the response, I took another look at my code and began seeing the error of my ways. _Why was I trying to mock up a HydratingResult in the first place? _
As a bit of background, my controller action iterates over a ResultSet, using \Zend\Paginator\Paginator. This class is either constructed using an Iterator or an ArrayAdapter; essentially it uses a traversable object.
Said another way, I'm always working with an array of model objects, no more no less and, the way I construct the Paginator object, it wouldn't matter if that was all I passed to it, even empty array.
I heard Ocramius' reply again:
Why are you trying to do that?
Good question, why was I? Honestly, I still had no answer.
Based on that, I revisited the code in question and simply created an array of model classes, calling
exchangeArray to initialize them. Then I set the generated array of model objects as the return value, from my mock object. Result? The tests ran perfectly.
Nb: for mocking, I'm a big fan of Padraic Brady's Mockery library. Works a treat, and easily interchangeable with PHPUnit's mock objects.
Why This Long Story?
I'll answer that with some questions. When you're testing your classes are you:
- Attempting to do too much?
- Attempting to mock code not required in the current context?
- Attempting to test code either multiple times or not in the correct place?
When you're testing a controller, you're not testing your model classes, right? The two are distinctly separate. Take for example, a simple case of iterating over a list of results in a controller action.
When testing the action, you assume that the classes which provide the data to your are fine, or at the very least, outside the domain of responsibility of the action's test; as they're covered by their own tests.
All you're concerned about here, is what happens with the information provided by them. For example, say you had logic in your view template, which would exclude certain records from being displayed, based on something in the user's profile.
Say you were listing departmental users, but the user could only view records for their department, specifically. However, if they were a manager, they could see records for all departments, not just their own.
You would then write, at least, two tests; one for a manager, and one for a standard user, likely using an XPath assertion, to check the response body.
If there's an error in the generation of the information however, you'd add more test in the model classes, not in the action tests. Right?
Can you see the separation of concerns? Can you also see how taking this approach makes life a lot easier?
When you're stuck - do you ask for help?
When you're stuck, what do you do? More than likely, go directly to either Google or StackOverflow. I'm not going to criticise, I do this too, quite often. I know countless people who do, as well. And there's nothing wrong with it.
But here's a further admission; I've been for many years, what Troy Hunt calls, the ghost who codes.
It's rather a catch-22. Whether you feel you've made a newbie mistake, a silly mistake, or feel you'll get flamed for even asking, you make up any number of excuses (_which you tell yourself are valid reasons_) for not asking someone for help.
If you do ask someone for help, invariably the next thing you do is start making qualifying excuses, such as "please don't judge me too harshly, but…"
I wrote it in a hurry / The budget was really tight / The manager was yelling at me to finish it
For these and many other reasons, we don't ask. But we should. After all, a cliché as it, how are we ever going to learn otherwise?
I've done and still do make excuses not to ask. But when I just asked people, the responses have been great and the rate at which I've improved has been staggering.
For that reason, today I'm encouraging you to set your pride, ego and excuses aside and when you're stuck: ask for help.
Not everyone is so helpful as the one's I've encountered, but most people are. Just remember to ask a question directly and be a bit patient.
If you have a Gist link handy too, that will go a long way, as it's much easier to write one up, with a good description and then share a link to it, than to try and verbally describe it in the channel.
I appreciate today's post has involved no specific code samples, but they're important topics which need to be covered. What's your experience with testing and asking for help? Share your thoughts in the comments.