A few definitions before diving in (the title is a mouthful):
- BDD = Behavior Driven Development, which is a more top down approach to Test Driven Development (TDD)
- Behat = Tool for implementing BDD written in PHP
- Selenium = open source browser testing tool, which can be used with behat as an addon through the Mink extension
- Docker = open source virtual machine tool to emulate other operating systems
- xampp = open source web hosting software for any (X) operating system containing Apache, Mysql (switched to MariaDB now, but at least it still starts with M), Perl, and Php
Nobody wants to be surprised by bugs after launch and manual testing is a painfully tedious process. Fully automated testing of web applications is like the holy grail. Unit testing can be useful, but ideally integration and even performance and user testing are automated as much as possible. I was excited to learn about BDD as a top down approach to test development as opposed to traditional TDD which is more of a bottom up approach.
I found behat while looking for a way to do BDD in php. I first tried following the behat quick start but I had to modify some steps. First, I had composer installed on my pc already (from getcomposer.com using the windows installer) but I had to change step 1 from:
php composer.phar require --dev behat/behat #maybe this works in linux but not on my pc
to:
#make sure composer is in your path so this will find it - the windows installer does that for you composer require --dev behat/behat
Whenever I saw “php composer.phar” in the quick start for behat I replaced it with “composer” and it worked for me.
The basic quick start example got me started with behat, and I found the PHPUnit appendix on assertions to be helpful in order to create my own tests. But I wanted to control a web application, so I then reviewed the documentation about using Mink with Behat.
I was successful in getting the behat feature/scenario driven testing to work with the Goutte driver, but for some reason (actually several errors I could not get past easily) I was not able to get selenium to connect properly on my pc using the latest jar file for chrome or firefox webdrivers (and trying several suggestions on stack overflow for people with similar challenges). That means I could only test using the headless driver. I needed to use both browser and headless browser testing because there are always javascript events and actions which occur on the page, and what seemed to be the problem is I was using windows locally instead of linux.
While researching how to get the chrome driver to work with selenium I saw a mention of using docker to avoid environment and software version mismatch issues. That led me to an article explaining how to use docker with selenium. This article was very valuable for me (I just followed the windows instructions, ignoring the ubuntu steps), as it gave me a way to get past the confusing driver downloads and behat configuration to try and get them working with selenium in order to test a website with Chrome or Firefox.
In order to control the browser in the docker container (and watch it using TightVNC as mentioned in the docker article) I setup the following in the MinkExtension section of my behat.yml file (port 4445 is mapped to docker following the article instructions, and the ip is from the docker-machine ip command as mentioned in the article):
selenium2: wd_host: 'http://192.168.99.100:4445/wd/hub'
The next challenge was how to connect to localhost (I do my testing in xampp on my PC first because that’s where I develop) from within docker. Using ipconfig on my pc I was able to find the internal network IPV4 address for my PC on my wifi network (if you are hard wired look for the ethernet connection address). Then I updated this line in my behat.yml file to use it for testing within docker:
base_url: http://192.168.0.101/dsdistribution/ #this ip is from the wifi ipv4 address from ipconfig
My full behat.yml file then became this (I initially had ssl connection issues with goutte so I had to turn off the verification):
default: extensions: Behat\MinkExtension: browser_name: chrome #I want to test with chrome for now, I'm using that docker image # base_url: http://en.wikipedia.org #this is an example of an external address to test behat with # base_url: http://localhost/dsdistribution/ #this doesn't work from within docker container base_url: http://192.168.0.101/dsdistribution/ #this ip is from the wifi ipv4 address from ipconfig on my PC goutte: guzzle_parameters: verify: false selenium2: wd_host: 'http://192.168.99.100:4445/wd/hub'
The dsdistribution folder is the webroot of the new application I was developing in my local environment.
My next challenge was to connect to the database on localhost from within docker. I first updated the host to be 192.168.0.101, but then my root with no password user did not have permission to connect. I found a recommendation for uncommenting a line in c:/xampp/mysql/bin/my.ini and setting it to allow remote connections:
[mysqld] ... # Change here for bind listening bind-address='0.0.0.0' #this allows any address to connect to mysql, only safe if network is internal
I just confirmed that my public IP does not have port 3306 open using a port forwarding tester at yougetsignal.com/tools/open-ports/, this is standard for home networks on a PC but it’s good to check anyway.
Actually the new bind address alone did not fix my connection to the database on localhost from within docker (even after restarting apache, mysql, and even xampp itself). There were too many rules in the mysql.user table for the root user so for some reason it was denying access using the ‘remote’ docker host. So the next step was to create a new user using the mysql command line interface (after logging in as the admin user I named root) like this:
CREATE USER 'behat'@'%' IDENTIFIED BY 'behat'; GRANT ALL PRIVILEGES ON * . * TO 'behat'@'%';
I planned to use this new behat user for testing all my applications locally so I gave it all privileges on every database. Once I followed that last step I just had to update my db connection file in my web application to have a clause like this:
if ($_SERVER['HTTP_HOST']=='192.168.0.101'){ $servername = '192.168.0.101'; $username = 'behat'; $password = 'behat'; }
Then I was able to open TightVNC (as directed in the docker article) and watch my behat test scenarios execute after running a command like this in a dos prompt (running from where I installed behat, pointing to a specific feature file to test):
c:\xampp\htdocs\behat>vendor\bin\behat features\productType.feature
I was doing this with @javascript added above my rather large testing scenario (which logs in and interacts with a form), I don’t think goutte will work within docker (at least not with any image I see in the list at hub.docker.com/u/selenium/), so I’ll need to change the base_url parameter back to localhost for the headless testing to work again. I don’t know if I can set the base_url within a driver section yet. I will update this post when I figure that out, but please leave a comment if you know already.