Presented at Drupal Camp Chicago 2012
Michelle Krejci details how Promet has used Jenkins, PHPUnit, and Selenium to automate our current continuous integration process so you can begin to start automating your QA testing today. She then outlines how Promet has begun to also include Phing and Chef to run PHPUnit tests on custom modules as part of test driven development. Finally, she looks at the challenges to running user acceptance tests on a Drupal installation and moving the Drupal community away from SimpleTest towards PHPUnit testing.
In short, this is an overview of what works, what doesn't, and why this is important to the Drupal community.
3. Exposition: characters
• Selenium*
– a.k.a. Selenium IDE
– a.k.a. Selenium RC
– a.k.a. Selenium Webdriver
*Actually, these are not at all the same. To wit:
Selenium IDE = development enviroment to create scripts
Selenium RC = Refers to Selenium 1 executable
Selenium Webdriver = Selenium 2 executable
4. Exposition: characters
• PHPUnit
– a.k.a. the testing standard of the
PHP community
– a.k.a. the successor of SimpleTest
5. Exposition: characters
• Jenkins
– a.k.a. Hudson
– a.k.a. A developer‟s most
dependable butler
6. Exposition: characters
• Chef
– a.k.a. An opscode product
– a.k.a. the answer to all your
server-configuration-related
prayers
7. Exposition: characters
• xvfb
– a.k.a. X virtual framer
– a.k.a. what you need to run
Selenium on a headless
8. Exposition: characters
• Phing
– a.k.a. Phing Is Not GNU make
– a.k.a. Apache Ant for PHP
– a.k.a. a build system for PHP
– a.k.a. what you will need to
run deeper regression, metric,
and user acceptance testing
9. Exposition: the setting
– During development
Lots of clicking
– After every update
– If someone somehow has the
time: on a regular basis as
part of on-going support.
10. Development: rising action
The Solution: Roll Call Testing
– Use Selenium scripts to verify that elements
such as ids, divs, and text are present on a
regular basis (e.g., after updates, during
development, on a regular basis)
11. Development: rising action
Roll Call Tests
– Each page gets its own test.
– Each test must start and end at the
home page.
• Verify that all blocks and links are
present using the VerifyElement
command.
• Do not use “assert” or this will halt all
tests if element or text is not present.
13. Development: rising action
Selenium IDE
1. Starting point.
2. Controls
3. Record actions (not needed for
creating roll call tests, recommend using
right click and select command)
4. Name of tests (should be named
after page)
5. The display
6. View and edit selected
command
7. Reference of commands
14. Development: rising action
Tools needed to run tests locally
– Selenium webdriver
– PHP Unit
– PHP Unit Selenium Exstentions
• Installing Selenium Webdriver:
– http://www.danstraw.com/installing-selenium-
server-2-as-a-service-on-ubuntu/2010/09/23/
• Installing PHP Unit & Extensions:
– http://www.phpunit.de/manual/current/en/install
ation.html
15. Development: rising action
Start Selenium Webdriver: In a new tab,
run command phpunit*:
*In this example, I am using a bootstrap
file. I have created a parent class that
each page extends. In the parent class, I
test the branding elements of each page.
17. Development: rising action
Add a ruby build script:*
#!/user/bin/env ruby
current_dir = File.dirname(__FILE__)
bootstrap_file = "MCMC.php” //if parent class
build_dir = "build"
Dir[File.join(current_dir), '*'].each do |file|
puts file
if file != bootstrap_file and File.file?(file)
cmd = "phpunit --log-junit
#{build_dir}/#{file}.selenium.xml /*--bootstrap
#{bootstrap_file} #{file}*/"
print cmd
system(cmd)
end
end
*No. It does not need to be ruby.
19. Development: rising action
Configure Jenkins Server:
– Add xvfb
There is a script in /root that has the commands to start the selenium service w/
Xvfb in a script named „start_selenium.sh‟
– Install the following plugins
• analysis-core
• Analysis-collector
• Checkstyle
• Dry
• Phing
• Plot
• pmd
31. Development: the resolution
Let‟s make it work.
• protected function drupalCreateUser($permissions = array('access comments', 'access content', 'post
comments', 'skip comment approval')) {
• // Create a role with the given permission set.
• if (!($rid = $this->drupalCreateRole($permissions))) {
• return FALSE;
• }
• // Create a user assigned to that role.
• $edit = array();
• $edit['name'] = $this->randomName();
• $edit['mail'] = $edit['name'] . '@example.com';
• $edit['roles'] = array($rid => $rid);
• $edit['pass'] = user_password();
• $edit['status'] = 1;
•
$account = user_save(drupal_anonymous_user(), $edit);
• $this->assertTrue(!empty($account->uid), t('User created with name %name and pass
%pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])), t('User login'));
• if (empty($account->uid)) {
• return FALSE;
• }
• // Add the raw password so that we can log in as this user.
• $account->pass_raw = $edit['pass'];
• return $account;
• }