If we're going to do functional testing of the various pages in our application we'll need to be able to navigate to the pages in question. The DrupalWebTestCase's built in browser provides us with two different ways to navigate our site. In this lesson we'll explore using the DrupalWebTestCase'::drupalGet()
, and DrupalWebTestCase::clickLink()
functions in order to navigate throughout our application. The former is useful when we know the exact URL of the page we want to navigate to and the latter is useful when we don't necessarily know the URL but we know which link to follow to get there.
I like to think of DrupalWebTestCase::drupalGet()
as typing an address into the URL bar in my browser. I know the page I want to navigate to so take me right to it. DrupalWebTestCase::drupalGet()
can be used inside a test case like so:
public function testHelloWorld() {
$this->drupalGet('helloworld');
}
This points the SimpleTest internal browser to the helloworld
page on our site, and loads the HTML content of that page into the buffer so we can perform assertions on its content.
DrupalWebTestCase::clickLink()
on the other hand is more like looking at the content of the page, reading through the links in the navigation menu and then deciding that you would like to click on the one labeled "Hello World". You don't know what address it's going to take you to, but that's okay because what matters is that you end up on the page. DrupalWebTestCase::clickLink()
operates by scanning the SimpleTest browsers buffer of the current page's HTML content for any link whose label matches the value provided.
public function testHelloWorld() {
$this->clickLink('Hello World');
}
This would have the effect of clicking on the following link:
<a href="helloworld">Hello World</a>
Or really, just extracting the value of the href attribute from that a tag and feeding it to DrupalWebTestCase::drupalGet()
.
I find the best reference for these helper methods is located in the Drupal.org handbook. https://www.drupal.org/node/265762
Additional resources
In this lesson we'll write a hello world test that navigates to the front page of a standard Drupal installation and verifies the existence of the text, "No front page content has been created yet.". This will allow us to walk through creating a .test file, adding it to our modules .info file, as well as cover the basics of extending DrupalWebTestCase
to write our own test case. For this lesson we'll use the helloworld sample module as a starting point and expand it to add a very basic test. The goal being to gain a better understanding of how the SimpleTest module discovers, and runs our tests. Then in future lessons we can expand on our test case to add more robust features.
All SimpleTest tests are an extension of either the DrupalWebTestCase
(we'll focus on this one), or the DrupalUnitTestCase
base classes. There are two pieces to the discover process. Writing a new class that extends one of the base classes, and then declaring the file that contains this new class in your module's .info file so that the file and it's code are indexed and inserted into the registry.
Create a new file inside the helloworld module's directory: tests/helloworld.test. The convention is to name files that contain test classes with a .test extension. Similar to how module file, despite being purely PHP, are named with a .module extension.
Then we'll add a basic skeleton test class like so:
class HelloworldTests extends DrupalWebTestCase {
/**
* Metadata about our test case.
*/
public static function getInfo() {
return array(
'name' => 'Hello World',
'description' => 'Tests for the Hello World module.',
'group' => 'Hello World Group',
);
}
}
Our class includes a getInfo()
method, it's required, and it returns a simple associative array with meta-data about our test. This information is used by the SimpleTest UI and other test runners to provide human readable information about our tests and to do things like group like tests together. When you view the lists of tests in the SimpleTest UI, you're seeing the 'name', and 'description' provided by your test classes' getInfo() method.
All tests should have a setUp() method as well. This method is called by the test runner after Drupal has been installed, but before our tests are run. It gives us the chance to perform any additional configuration on the environment within which the tests are running. In our case, we need to enable the helloworld module since it's not part of the default install profile. We can do that by delegating to the parent classes' setUp() method and passing an array of modules we want enabled. That might look something like this:
/**
* Perform any setup tasks for our test case.
*/
public function setUp() {
parent::setUp(array('helloworld'));
}
Finally, we need to provide at least one method whose name starts with "test". This method, and all others whose name starts with test will be called in succession by the test runner and are expected to contain the assertions that make up our individual test.
public function testHelloWorld() {
$this->drupalGet('helloworld');
$this->assertText('Hello World. Welcome to Drupal.', 'The page content is present.');
}
This test will navigate to the url /helloworld, and then verify that the text "Hello World. Welcome to Drupal.", is displayed on the page.
In order for our test to show up in the SimpleTest UI we need to add it to the registry. We can do so by modifying the helloworld.info file and adding the line files[] = tests/helloworld.test
to the file. Then clear the cache and Drupal should discover your new test case and allow you to run the tests either through the SimpleTest UI or with drush.
Additional resources
DrupalWebTestCase documentation
Written tutorial based on this video
Much of the functionality that web based applications provide is in the form of an HTML form. Enter some data into the various fields, press the submit button, and see what happens. The DrupalWebTestCase provides a handful of methods to aid in interacting with forms. In this lesson we'll take a look at filling out various types of form fields, submitting forms, and validating the result.
We'll start by navigating to a page that contains a form and simply verifying that the appropriate fields are found on the page.
$this->drupalGet('helloworld/form');
$this->assertFieldByXpath("//form[@id='helloworld-cake-form']//input[@name='name']", '', 'The name field is present.');
$this->assertFieldByName('choice', 'cake', 'The choice field is present.');
This code checks for the input name="name"
field, and the select name="choice"
field, and ensures they are present on the page at hellworld/form.
Then we'll use the DrupalWebTestCase::drupalPost()
method to fill in, and submit, a form. Here's an example:
$data = array(
'name' => '',
'choice' => 'chicken',
);
$this->drupalPost('helloworld/form', $data, 'Submit');
$this->assertText('Your name field is required.', 'Name field is required.');
This code fills in the form on the hellworld/form page, and then clicks the submit button using the DrupalWebTestCase::drupalPost()
method. The first argument is the URL at which the form lives, the location to which the HTTP POST request will be sent. The second argument is an associative array of values for the form fields. The keys are the names of the input elements, and the values are the information we would like to enter into those fields.
In this lesson we also make use of the $this->assertFieldByXPath()
method, which allows us to select a form element on the page using an XPath selector. In our case, we need to do this because there are actually two input element on the page with their name attribute set to "name", and we need to make sure we're testing the correct one. If you're not familiar with XPath it's worth taking some time to review the syntax since chances are you're going to end up using it at some point when working with SimpleTest.
For more information about these helper methods checkout the API Functions handbook page.
Additional resources
January Tech Update
Blog postHappy new year, members! We worked hard during the holidays on some important tech improvements. Here’s a quick overview.
Podcast Episode 55: The RESTful Module
Blog postLooking to learn more about the RESTful module for Drupal? Look no further than our podcast #55 — The RESTful module. Host Kyle Hofmeyer joins Mateu Aguiló Bosch and Amitai Burstein to talk about the RESTful module they both maintain and how it is different from other solutions out there. Also on the podcast, conversations about why they choose GitHub as the platform of choice for hosting the module, Drupal 8, and what it is like to have two maintainers for a single module.
In this tutorial, you will learn how to enable responsive layouts in your theme by adding a viewport meta tag to your html template file. For more context and a demonstration, watch the free video, Adding the Viewport Meta Tag to html.tpl.php in the Getting Started with Responsive Web Design in Drupal series, now on Drupalize.Me.
Today we kick off a new series on writing tests for Drupal 7 using the SimpleTest framework included with Drupal core. This week introduces the concept of testing, some terminology specific to the SimpleTest framework, and walks through locating and running existing tests.
Developers write tests for a variety of different reasons and understanding why, and how, writing tests can improve the quality of our code and our sanity helps motivate ourselves to write tests. The tests we write usually take one of two forms. Functional tests are used to test an applications behavior. When I click this link does it take me to the about page? Unit tests are used to verify the logic within a small segment, or unit, of code. When I call this function that is supposed to return a link with these specific parameters does it return the correct data? There are a ton of reasons that investing time and resources into writing automated tests is a big win in the long run and in this lesson we'll cover a bunch of them, including:
- Eliminate repetitive tasks.
- Improve overall stability of an application.
- Ensure critical paths & edge cases function.
- Reduce (not eliminate) the number of tests that need to be performed manually.
Types of Tests
There are are two main types of tests and we're going to be writing. Functional tests, and Unit tests.
Functional tests, also known as integration tests, test the functionality of a particular aspect of an application by simulating normal interaction with that system. In the case of web software this usually refers to simulating a user navigating and interacting with the website as if they where using their browser, keyboard, and mouse.
Unit testing is the art and practice of taking a small portion of code, a unit, and subjecting it to programmatic tests to prove its correctness. For example, testing a function that performs unit conversion by comparing a known input with an anticipated result.
UPDATE: This video references qa.drupal.org which is no longer in use. On Friday, October 23rd 2015 - qa.drupal.org received and processed it's last
test. The qa.drupal.org testbots have now been superseded by DrupalCI - and test results are now displayed directly on Drupal.org. While the mechanics are different now, the end result is still all patches to Drupal core are automatically tested.
Before we get into writing our own tests we need to enable the SimpleTest module, and while we're at it we may as well talk about the various configuration options it has. And then why not run a few of the tests that come with Drupal core as a way to learn how to discover and run all the tests, a group of tests, or even an individual test case.
The SimpleTest module comes with Drupal 7 core, but is not enabled by default. So we can start by enabling the module. On the administration page for enabling modules the module is listed as "Testing" instead of SimpleTest which can be a bit confusing. The the UI you'll almost always see the name "Testing", but in the code, documentation, and at the command line it's generally referred to as SimpleTest.
Once the module is enabled there are just a few new permissions and configuration options to cover. The new 'run tests' permission, and a couple of settings related to enabling verbose output for debugging of tests. Make sure you check them all out.
Tests can be run in a variety of different ways, either from your browser using the Testing module's UI, or at the command line with drush. We'll cover both of them in this lesson. And in doing so we'll look at how to run a group of tests, or an individual test case, and the output that's generated by running tests.
I've also got a module (which you can download from this page) that contains a couple of failing tests. This way we can run a test case that fails, and see what the output looks like from a failure. SimpleTest outputs some helpful debugging information, and with the verbose option enabled will even save the generated HTML of each page the SimpleTest browser saw so that we can review it after the test run is complete.
If you're not familiar with drush checkout our series on using drush.
By the end of this lesson you should be able to enable and configure the simple test module and run some tests.
Example drush commands
Run all tests in the "Block" group:
drush -l http://localhost/demos/simpletest-7x/docroot test-run Block
Run just the "BlockHashTestCase" test case:
drush -l http://localhost/demos/simpletest-7x/docroot test-run BlockHashTestCase
Note: as of Drush 7 the drush test-run
command is no longer part available. See https://github.com/drush-ops/drush/issues/599.
You can either, use Drush 6, or use the scripts/run-tests.sh
scrip that comes with Drupal core. See the documentation for the run-tests.sh script for more information.
SimpleTest is the tool that Drupal 7 core uses for discovering, and running all of it's tests. SimpleTest is also the name of the system that is used to write tests that can be run by the SimpleTest module. Understanding how SimpleTest locates the code that makes up our tests, and then executes that code, is an important part of understanding how to write tests. In this lesson we'll be discussing the pieces that make up the SimpleTest toolkit, some related terminology, and some best practices for dealing with SimpleTests in Drupal.
Methodology
Some best practices to keep in mind when working with SimpleTest and writing tests.
- Tests should always be run in a separate environment from your production site.
- Each test case sets up a completely new Drupal env, as such your test case needs to prepare the new environment as appropriate for your tests. Things like enabling any necessary modules, creating dummy content, and users etc. For this reason, it's best to have tests that require a similar setup be part of the same test case for better performance.
- After a test case is run the environment is torn down and started fresh for the next test case. This ensures there is no contamination between test cases and that each test can count on knowing exactly what to expect from the environment it's running in.
- SimpleTest is not always ideal for doing functional testing of your existing site. Drupalize.Me for example, we want to test that our About page is visible. But that's content, and doesn't live in code anywhere so creating it at install time can be tricky: We'll cover this scenario in more depth later.
- Always test both sides of the problem. If your site only allows authenticated users to make comments, and you write a test to make sure authenticated users can comment you'll also want to make sure you write a test that verifies that anon. users can not comment. If you're checking that a value exists under specific conditions, also verify that it doesn't exist when those conditions are not met
Terminology
SimpleTest: The original name of the module used for testing in Drupal. Though it's now called just "Testing", the terms are used interchangeably. Based on the PHP SimpleTest library.
Test Case: A set of conditions under which the tester will determine whether the system being tested satisfies requirements and works correctly. A Test case defines the environment in which a test or set of tests will run. Including preconditions such as what modules are enabled, what content exists, etc. It is assumed that these same preconditions are true for all individual tests within a test case.
In Drupal, all test cases are classes that extend the DrupalWebTestCase
, or the DrupalUnitTestCase
class.
Test (Test method): Each individual test checks the functionality of one discreet thing. Example, if our test case is "User cancellation" there are many variations of cancellation that we need to test. Cancel the account and keep their content, cancel the account and delete their content, don't allow cancellation of the account with UID 1, etc. Each of these individual tests is checking one piece of functionality with the test case.
Assertion: The smallest unit of a test. An assertion is used to check wether or not a given condition is true in the current context.
Additional resources
Testing, and quality assurance, are an important part of maintaining high-quality software. Together they ensure that the code we write functions as it is intended to, and that changes made later on do not introduce any regressions into our application. Its tedious work and generally involves coming up with a list of tasks that prove a specific feature is working, and then repeating the list of tasks every time a change is made.
Automated testing is the process of writing code to reduce the number of things we need to do manually when testing our software. That list you created to ensure your software is working correctly is likely something that can be easily automated with a testing framework like SimpleTest. Doing so can dramatically reduce the amount of time it takes to perform QA, and improve the overall stability of our applications.
The SimpleTest testing framework was introduced into Drupal core early on in the Drupal 7 development cycle. Since then it has had a profound impact on the way our community develops Drupal. Core now contains a whole suite of tests that cover somewhere in the neighborhood of 75% of the softwares functionality. Every time a new features is introduced, or a bug is fixed, this battery of tests confirms that the fix to the date formatting function didn't inadvertently break something else.
In this series we'll learn to write our own automated tests for Drupal 7 using SimpleTests. We'll walk through enabling SimpleTest, and running one or more test cases with both the SimpleTest UI, and with drush. Then we'll cover some of the related terminology like the difference between functional tests and unit tests. As well as discuss why testing is an important part of development and worth the investment.
Then we'll walk through writing a test case and a set of assertions in orders to verify that various features of our site are working. We'll use functional tests based on the DrupalWebTestCase
class, which simulates a browser navigating our site and clicking on links, and filling out forms. And the DrupalUnitTestCase
class, which allows us to write super fast unit tests for the business logic contained within our code.
After watching this series you should be able to:
- Install the SimpleTest module and run the tests included with core.
- Define and know the difference between functional tests, unit tests, test cases, and assertions.
- Create a new test case so that SimpleTest can discover and run your tests.
- Write assertions that test the validity of your application in a given state.
- Use the test browser to click on links and navigate to pages on a site.
- Fill out and submit forms and AJAX form elements using the test case browser.
- Handle file and image uploads in test cases.
- Write unit tests, and understand how the workflow for running unit tests differs from that of functional tests.
- Use the SimpleTest 7.x-2.x module from contrib to test existing websites.
This series assumes that you have basic understanding of Object Oriented PHP, and are familiar with Drupal module development. If you need to brush up on your module development watch our series on Drupal 7 Module Development. And, while not required to run tests, we do make use of drush in this series so knowing how to run drush commands is helpful.
Writing tests can be a challenging. But it doesn't have to be. And the benefits of doing so are huge. Especially as our web applications become more complex, as our teams grow, and as the multitude of ways in which people interact with our web applications changes constantly. Spending some time learning how to write tests will not only make your applications more reliable, it'll also make you a better developer.
Additional resources
Changes in the Form API in Drupal 8
Blog postIn my previous post, I documented the first of my Adventures in Porting a D7 Form Module to Drupal 8. In that article, I documented how I used the Drupal Module Upgrader to convert my Drupal 7 module, Form Fun, to Drupal 8 and what I learned along the way about how Routes and Controllers replaced hook_menu
, and what I gleaned from change records about other API changes. This article is a continuation of that post, so you might want to pop over and give it a read so that you're up to speed with what we're doing here.
We recently completed making updates to our incident response plan for Drupalize.Me and I wanted to share some of what we learned along the way, and help you write your own. An incident response plan is all about being prepared. So that in the moment, under pressure, when everything that could possibly go wrong is going wrong, you can remain calm, cool, and level-headed. If you've ever had to write a social media message, or respond to a support request during an un-planned site outage you know how easy it can be to misstep—even if your intentions are good.
Release Day: Wrapping up PHP Part 3
Blog postWe've covered a lot of material in the three parts of PHP for Beginners. Today we are wrapping up Part 3, on databases. This week's tutorials are more focused on the PHP side of things, taking a look at PHP function arguments and query parameters, along with an example of how PHP function scope works. We finish up this series with a very important lesson on SQL injection attacks, and how to make your PHP database code secure.
January Content Update
Blog postHappy 2015, members! We’re welcoming this new year with new Drupal tutorials. Here’s what you can expect over the next few weeks.
Staring at a blank screen, notebook, or any other space flooded with emptiness can conjure feelings of worry, confusion, and definitely fear. Yet this is a ritual anyone who considers themselves a creative willingly puts themselves through on a regular basis. Some may dread these less than pleasant feelings, but I am sure there are also many who embrace them, and I am one of them. Full disclosure, creating something is a scary process for me, and that's ok. From beginning to final product there are plenty of uncomfortable moments that I find extremely beneficial and rewarding to a successful creative process. Hopefully after I share how these often referred to as negative emotions are helpful, you, too, will see how essential they are to your creative process, and why they should be embraced and not avoided.
This week we're continuing PHP for Beginners Part 3 , which is working with databases in PHP. Last week, we started off my covering the fundamentals of SQL and working with a MySQL database. Today we hook this up with the PHP site we're building. Additionally, we have a bonnus lesson YAML.
YAML, which stands for YAML Ain't Markup Language, is a human-readable data serialization format that's been widely adopted in a variety of use cases in Drupal. Anyone wanting to write modules, or themes, for Drupal will need to understand YAML syntax. Even site builders are likely to encounter YAML at least in passing as YAML is the data-serialization format of choice for Drupal's configuration management system. Good thing it's pretty easy to learn even with the most basic of programming backgrounds.
This tutorial will look at the YAML data format and provide examples of how to write and read YAML. Starting with an introduction to the language's syntax and some of the strengths of YAML. Then looking at the difference between scalar data types like strings and integers, and collection data types like lists and associative arrays.
Since YAML in the Drupal world is read into PHP and ultimately becomes a PHP data structure that we can use in our own code we'll also look at how the YAML we write in a .yml file is represented in PHP data types. To do this we'll use the YAML Sandbox module that provides a handy textarea into which we can type YAML and have it parsed into PHP data structures.
Learning objectives
- Explain what YAML is and its strengths as a data serialization format
- Create scalar key/value pairs in YAML
- Create lists, and associative arrays using YAML collections
- Understand how the YAML you write is represented in PHP
Tips
- In Drupal, use the .yml extension and not .yaml
- Ensure your code editing application is configured to use spaces (preferably 2 spaces, as per Drupal coding standards), not the tab character when the TAB key is pressed. If you have tab characters in a YAML file within a Drupal environment, a fatal PHP error will be thrown and you'll see a White Screen of Death (WSOD).
- Copy and paste from an existing YAML file to ensure the formatting is correct, and edit from there.
Additional resources
- http://www.yaml.org
- YAML Sandbox module
- Find other tutorials and external resources related to YAML on our YAML topic page (Drupalize.Me)
Drupal 8 Upgrade Path
Blog postAll this excited talk of Drupal 8 has a lot of people dreaming of the day they get to start working with it. Some people get to build new sites from scratch all the time, but a lot of Drupal work out there is maintaining and upgrading existing sites. How will the Drupal 8 upgrade path work, and will it be as shiny as Drupal 8 itself? Well, upgrades will be radically different in Drupal 8, and I'd say it has all the shiny you could possibly want.