Automated Testing QRCodes in Selenium WebDriver
How to test QRCodes on a webpage
QR Codes are becoming a more and more common method of link sharing, but how can we test that the QRCode you generated goes where it needs to go?
QRCodes are a barcode that can be scanned with a camera app or dedicated scanner. They have been growing in popularity because of COVID-19 and are commonplace now (at least in Australia!). Most websites and apps use QR Codes as a shortcut link (e.g. scan this QR Code to order for Table 12).
This tutorial will show you how to test that a valid QRCode was created and verify it successfully goes to a particular URL.
Tutorial Website
The WhenWise app has a QRCode for directly booking an appointment with a staff member. The below QRCode is an example on a server running locally for testing purposes (if you try to scan it, it won’t work).
I plan to write a test script to verify the QRCode generation.
Test Design
- Save the QR Code image
Selenium 4 supports saving an individual page element to an image. Typically, generated QR codes are in SVG format, not conventional bitmap images formats such as PNG. - Read the QR code
We need to decode the QR code to text. This can be achieved from a library.
(The availability of many libraries is one great benefit of writing automated tests in a powerful scripting language such as Ruby.) - Verify the URL
Determine the expected URL first, then compare it with the decoded text from the QR code.
Prerequisite
I use qrio, a QR code decoder in pure Ruby. Install it on your command line with:
gem install qrio
QRio can load an image of a QR Code and then extract its text. Example usage from the documentation is:
require 'qrio'
qr_decoded = Qrio::Qr.load("qr-code.png").qr.text
Save the QR Code image
To save the QR Code, we can use Selenium 4’s built-in elem.save_screenshot(output_file_path)
function.
To save our QR Code we would run this on the page with the code:
# defining a relative path for saving the image to
tmp_dir = File.expand_path File.join(File.dirname(__FILE__), "..", "..", "tmp")
dest_image_file_path = File.join(tmp_dir, "qr_code.png")# locate the QR Code element
elem_qrcode = driver.find_element(:tag_name, "svg")# save the image
elem_qrcode.save_screenshot(dest_image_file_path)
If you execute this, the QRCode image will be saved under ../../tmp/qr_code.png
. Now we can start decoding it with QRio and do our verification.
Before we finish, I recommend doing some post-script clean up. During the test, we save the QRCode. However, we probably want to delete it after the test completes so we are not left with unnecessary files that could interfere with future test executions.
Complete Test Script
it "Verify QRCode" do
require "qrio"
visit("/human_resources/6")
human_resource_page = HumanResourcePage.new(driver)
expected_qrcode_url = driver.find_element(:id, "booking-qrcode")["href"]
puts "Expected => #{expected_qrcode_url}"# save the qr code image
tmp_dir = File.expand_path File.join(File.dirname(__FILE__), "..", "..", "tmp")
dest_image_file_path = File.join(tmp_dir, "qr_code.png")
elem_qrcode = driver.find_element(:tag_name, "svg")
elem_qrcode.save_screenshot(dest_image_file_path)
# read the qr code
qr_code_text = Qrio::Qr.load(dest_image_file_path).qr.text
puts "Scanned => #{qr_code_text}"# assert qr code url is correct
expect(qr_code_text).to eq(expected_qrcode_url)
end
Notes:
- Find the QR Code element
Here I used thetag_name
locator, as there is only one SVG image on the WhenWise site. If there are more, usefind_elements(:tag_name, ...).last
or an XPath locator. - Save the QR code image
Using Selenium’selem.save_screenshot 0
(v4).
Note: I suggest using a relative path, it’s good practice. - Decode the QR code from the saved image
Using theqrio
library. There are other libraries in Ruby (and other languages as well), but I have not encountered any problems with QRio.
Test Execution (in TestWise)
Below is a screenshot after execution in TestWise.
In the console at the bottom right, the expected and scanned QRCode’s URLs are printed. Both are the same — so the test passes successfully!
The full execution can be seen in the gif below.