Hundreds of thousands of software applications are scheduled for migration from Python 2 to Python 3. The deadline is approaching fast: Support ends with 2019.
Abundant articles explain the importance of and techniques for this migration from a developer’s standpoint, including How to Port Python 2 Code to Python 3, Conservative Python 3 Porting Guide and official documentation for Porting Python 2 Code to Python 3, as well as several valuable video tutorials and other resources.
But how can testers help ensure a Python 3 migration is successful? Testing’s unique perspective has plenty to contribute.
Planning and preparation
First, recognize and promote the importance of planning and preparation. Programmers have a tendency to see programming as the solution to problems, and they often benefit from reminders to plan more strategically before the coding starts. A testing department can help developers clarify ahead of time such questions as:
- Is the product source migrated to Python 3 still expected to be able to run with Python 2.7 during a transition period? How long is that transition?
- Will the product source migrated to Python 3 be developed as a branch or on the main trunk of development?
- Once the migrated source goes into production, what will it take to revert to sources compatible with Python 2.7, if reversion is decided necessary?
- Do the testing sources need to migrate? How is their schedule coupled to that of the product sources?
- Can the deployment be targeted for a “quiet time” or “null sprint” when no functional changes are expected? Migration while trying to stay on top of other difficulties and priorities is asking for trouble.
- Will acceptance be on the same rhythm as other releases?
That last point deserves more explanation. Suppose the product to be migrated typically deploys a new release roughly weekly. Once deployments pass all automated tests in this hypothetical practice, they’re regarded as provisionally complete for another 24 hours, to give surprises time to surface. Although deployment schedules vary widely — from minutes to months — most organizations at least recognize these elements.
Should the move to Python 3 be on the same schedule as a “normal” deployment? Any rational answer depends on the comprehensiveness of tests. While a collection of unit tests and a few sanity checks at the integration level might be adequate for most releases, migration to Python 3 tends to bring out whole new categories of problems.
Anything to do with Unicode deserves careful inspection. Quite a few projects have thought their migration complete, then discovered problems only as foreign-language users began to exercise the application. Encoding dependencies pop up in places that don’t occur to the naive: CSV readers, pickles, cookie handlers, cachers and more. Defects or incompatibilities in these places usually have straightforward, backward-compatible resolutions, but someone needs to detect and identify them first. To do so for a release already in production is clumsy at best.
Does your team operate with automated tests? If it hasn’t made that step yet, can it install automated steps before the migration? If the product is a purely text-based library or service, maybe automation is straightforward and yields good coverage. On the other hand, if the product is a web, mobile or desktop application, only a specialized test automation tool can reduce the risk involved in a change as large as that from Python 2 to Python 3.
Ranorex, which underwrites this blog, offers a top-flight testing tool, Ranorex Studio; among other features, Ranorex Studio’s recorder generates customizable scripts in Python. Part of your analysis of the migration should be to define and plan adequate test automation well before deployment of the migrated source.
Another significant risk of migration has to do with performance degradation. Python 3 is mostly faster than Python 2, and it uses less memory for many important situations — when the source is idiomatic. Problems can arise, though, with subtle semantic shifts.
Python 3 often provides iterators in place of corresponding lists from Python 2; range() is an example. Iterators are generally far thriftier with memory but demand more time when re-created repeatedly. Essentially, expect a Python 3 version to be quicker and take up less memory, but perhaps only after a few adjustments.
A few other hazards of migration are well-documented. Python 2 computes 5 / 3 as 1, while Python 3 gives a result of 1.6666666666666667. In Python 3, the way to arrive at 1 for this calculation is with 5 // 3.
All these coding pitfalls have relatively straightforward solutions. Each one demands careful scrutiny though, and errors can combine to upend schedules — and it won’t only be coders who are affected. More even than technical tools or programming heroism, it’s important for testing and development departments to align expectations ahead of time.
Python is on its way to becoming the world’s most popular programming language, so the move to Python 3 will affect many such departments. Explicit plans, well in advance of programming changes, give testers time to prepare and practice with all the materials and tools they need to verify a successful migration