The definitive remote debug and unittest with PHPStorm guide: part 4
This post is part of the guide on how to setup your PHPStorm for remote debugging and unit testing. Click here for the beginning of this guide.
Remote PHPUnit testing
Unit testing should play a pretty big part in your daily PHP work. With the help of PHPUnit, writing and executing those tests is fairly simple. But in a remote environment we run into some issues which we try to solve in this blogpost. I’m not going to tell you the basics of unit-testing, as we assume you already are familiar with this. If not, There are lots of blog posts on this subject. Let’s assume you already have some unit-tests in place and a phpunit.xml file. This setup should be fairly obvious:
You can run your unit tests in a few different ways. If you have a SSH / shell connection to your virtual machine, you can easily run your unit tests on the command line by running the following:
Since your virtual machine has shared directory, it’s even possible to run your unit tests directory on your local machine using the PHPUnit and php versions of your local environment, but obviously this isn’t really recommended. If you happen to have projects running locally, it’s pretty easy to have PHPStorm running them, but running your unit tests directly from PHPStorm IN YOUR REMOTE ENVIRONMENT, requires a bit more work.
How PHPStorm does (remote) unit-testing
Remote unit testinging in PHPStorm works in a way that might not be obvious in first glance. Unlike XDebug, there is no “server” that communicates with the PHPStorm IDE, so there must be a different way to communicate.
What happens is that whenever you run a remote PHPUnit session, PHPStorm will upload a file called _intellij_phpunit_launcher.php to your project which acts as a server/proxy for running PHPUnit and sending the output back to PHPStorm.
Once uploaded, it will just execute this file by accessing it through your web-server. It literally uses your web-server as a proxy between PHPUnit and the IDE. A very clever solution and even though this system works pretty OK, it has some drawbacks when it comes to remote environments: first of all, since all our files are local, “uploading” a file seems weird.
Another issue is that this system does not work when you are working on a project that isn’t a web-app, as such application does not have a web-server and you cannot use this kind of method. As a workaround, you can create a “dummy” webroot inside the project that is accessible through a web-server, so this remote debugging still works. Unfortunately, unless PHPStorm will allow remote debugging through things like SSH clients, it’s the only way to use it for now.
Setting up your unit-testing
First we need to setup a configuration in PHPStorm. Just like we did with creating server path mappings, we go to the black triangle icon in our toolbar and select “edit configurations”. In our defaults section, choose “PHPUnit on server“.
At the top we see “server”, click on the “…” behind it and configure a so-called deployment server (even though we won’t be actually deploying something).
Set the name to whatever you want. In the configuration tab, select the type “In Place”. This tells PHPStorm that the files are already on the server, and nothing needs to be “deployed”. This is important, because this “tricks” PHPStorm in uploading the _intellij_phpunit_launcher.php file by just copying the file to its local directory.
At the “web server root URL”, type the root name of your server. Do not add a / at the end, but if you have your project inside a subdirectory, add that subdirectory here.
Next up, the mapping tab. In here we are doing pretty much the same thing we did before with the path mappings. Basically, it’s the same thing, but path mappings are for debugging, and these mappings are for deploying. We aren’t really concerned about deployment, since we don’t really deploy stuff, but we need a mapping to trick PHPStorm a bit. It won’t run our PHPUnit without a mapping. So, we create a simple mapping from our public root directory to the correct URL on the server:
The local path is where the _intellij_phpunit_launcher.php file will be stored “deployed”, since it must be accessible from a web browser, this path probably will be your public directory.. The web path will be the path as seen from a web browser. Below this you will see the project URL (which you have filled in the connection-tab before), and here you must add the path to where this file will be accessible. Most of the time, this will be a simple ‘/‘.
If you are thinking that this seems awfully close to creating the path mappings which we did earlier: you are correct. It’s pretty much the same thing, but it has a different purpose. The path mappings are for xdebug and phpstorm to make sure they can find the correct files, but these mappings are for phpstorm to find its _intellij_phpunit_launcher.php file. Normally, you would have more mappings and more complex setups when you are actually using this deployment dialog when doing direct deployments from phpstorm on for instance sftp servers. Since we aren’t doing this on vagrant and/or virtualized systems, we can keep things simple.
No need to add another mapping, so we can just apply/ok and close the dialog.
At this point, we are back in the dialog box where we were creating a “PHPUnit on server” configuration. Pay attention, because here is where we need to add some trickery in order to successfully use PHPUnit:
We’ve done the name and the (deployment) server, now it’s time for the configuration. First of all, we need to select XML-file radio button. This tells PHPUnit to run through a dedicated configuration XML file. It also means that you cannot really just test a single directory or single test file directly from PHPStorm (as most of the time, we map our test-files in the same structure as our php files, so we have /foo/bar/class.php that has its tests in /tests/foo/bar/class.php, if PHPStorm would know this kind of mapping, we could run straight away tests for only the specific file and give instant feedback on your tests while editting! Hopefully a new feature in an upcoming PHPStorm release…).
We select a XML file, but we must provide an alternative configuration file. THIS MUST NOT BE YOUR MAIN PHPUNIT.XML file. The thing is: PHPStorm will get confused in a bit between local and remote paths, as it isn’t aware that we are running on a virtualized system. At this point it thinks that we are doing everything locally.
For now, you must create a dummy XML file (which can be empty), inside your public directory and point it to there. It MUST be in the public root, otherwise PHPStorm get into trouble finding the file as it tries to locate the file LOCALLY. Trying to move this file outside your public path doesn’t work, as PHPStorm gets confused between the location of this file, and where it stores the _intellij_phpunit_launcher.php file. If there was an option to tell PHPStorm where to store the _intellij_phpunit_launcher.php file, this issue would be fixed, but for now, live with the fact that we NEED a dummy XML file :/
Next, we add some parameters that gets transmitted to our PHPUnit on the remote server. In our case, we only tell it that we are going to use a specific configuration (yes, indeed, it looks weird that we are providing a custom configuration here, but this is the only way to get it working!). The “-c phpunit.xml” tells PHPUnit that it will use this configuration (notice that i don’t add a path).
Last item in this dialog: the custom working directory: as PHPStorm is running the _intellij_phpunit_launcher.php file directly from the webroot (the public directory in our case), we must tell it to change the working directory to the root directory of our project. Which in my case is “..”.
Here we are tricking PHPStorm again, as you can only supply local directory names and not remote directory names. You can’t say /wwwroot/myproject here, because this directory is unknown to PHPStorm, and you can’t say /Volume/wwwroot/myproject here neither, as this might be accepted by PHPStorm, but now PHPUnit that runs on our remote system doesn’t know anything about this. Using relative directories here will work, as both parties understand this, even though it’s nothing more than a hack for now.
Once completed, this is all you need to do. Apply / close the dialog, and you will be able to select your phpunit configuration from the toolbar. Press the green play-button (of shift-F10 on a mac) to run, and you should be able to see phpunit running:
Some issues with “in place” connection type
There might be some issues when you are using the “in place” connection type as mentioned above. PHPStorm tries to generate the correct paths to deploy and run the _intellij_phpunit_launcher.php file. Unfortunately, when you have an environment where your root-path and web root path aren’t equal (which is the case when you work with pretty much every PHP framework out there), it can fail. Either it deploys in the correct directory, but fails to use the actual webroot to call the file, or vice versa.
If you run into trouble, you COULD try to change from “in place” to “Local or mounted folder”. Here you actually type the exact place where PHPStorm needs to add the _intellij_phpunit_launcher.php file:
The /public directory is the actual directory where PHPStorm needs to copy the _intellij_phpunit_launcher.php to. We need the “.” as well, so it will stay inside this web root. Also, now we can just tell it that this file is actually at the given web path of / (which makes sense: as the /public directory is actually the / on the webserver). However, we’re not out of the woods just yet. We need to tweak a little bit in the configuration main page:
I still haven’t got rid of the PHPUnit dummy file, and instead of pointing to the “..”, i must now add the full path to the public directory and append it with “..”. Another small but important part is that inside the test runner options, i’ve changed the path from “-c ../phpunit.xml” to “-c ./phpunit.xml”, from double to a single dot.
Remote PHPUnit in PHPStorm on virtualized systems is not easy. In fact, it’s amazing that it’s even possible in PHPStorm, as it doesn’t recognize these kind of environments fully. Granted, they are on their way and every new release brings us more and more features, but we really need to have a decent virtual setup where PHPStorm knows about the difference between our local and remote paths, have a smarter system that should allow us to override some items (right now PHPStorm does not allow us to run phpunit when we don’t specify a XML file, even though it’s a dummy file).
For now, this blogpost gives you a basic solution that is workable, but not ideal. Here’s hoping on new releases being more virtualized environmental friendly.
Next time we do something fairly simple: adding some code coverage information!