A Perspective on Code Refactoring from a Tester’s Point of View
‘Code Refactoring’ is a term frequently used to refer to the process of tidying up and reconfiguring code.
Recommended IPTV Service Providers
- IPTVGREAT – Rating 4.8/5 ( 600+ Reviews )
- IPTVRESALE – Rating 5/5 ( 200+ Reviews )
- IPTVGANG – Rating 4.7/5 ( 1200+ Reviews )
- IPTVUNLOCK – Rating 5/5 ( 65 Reviews )
- IPTVFOLLOW -Rating 5/5 ( 48 Reviews )
- IPTVTOPS – Rating 5/5 ( 43 Reviews )
This tutorial will delve into what code refactoring means, its significance, and its effect on various team members of a project. It will also touch on the reason why testers should have a good understanding of code refactoring.
In addition, we will review various real-world case studies for a clearer grasp of the concept.
Table of Contents:
Getting Introduced to Code Refactoring
Let’s begin by defining what we mean by code refactoring.
Code refactoring involves enhancing the structure of code or databases without affecting current functionality. Its primary goal is to modify complicated and ineffective code into a simpler, more efficient, and easier-to-comprehend version.
Recently, code refactoring has been growing in popularity, particularly among Agile software development teams. Given the limited time to create new features or extend current functionalities, clean, easy-to-understand, and maintainable code is vital in meeting project timelines.
Why is Code Refactoring Needed
A common question is, why bother with code refactoring if it doesn’t change the application’s functionality? Plenty of reasons explain why a module or piece of code might need refactoring:
- Detection of code smells
- Tackling technical debt
- Following Agile software development practices, among others
We will discuss these points more in the upcoming sections.
#1) Recognizing Code Smells
Just as experiencing a foul scent from food signals potential spoilage, code smells can indicate possible problems within the source code.
Common patterns of code smells include:
- Unnecessary or repeated code
- Variables that aren’t utilized
- Code designs that are overly complex
- Classes with negligible functionality
- Intricate and too many conditional statements and loops
- Inflexible code that results in changes in multiple sections
Code smells become more conspicuous as the codebase expands, and eventually, they start affecting code development, maintenance, and even the overall system performance in extreme cases.
#2) Dealing with Technical Debt
To achieve desired results, developers often resort to shortcuts during software development due to constraints in time and resources.
Assume a situation requiring the addition of a new feature to an existing module. The team identifies two possible methods: Method A necessitates two sprints for implementing and aligns with long-term targets, while Method B delivers the feature in five days using complicated hard-code workarounds.
If there’s pressure to meet stringent timelines, the team might choose Method B as a temporary solution, with plans to address it later. However, this choice results in technical debt.
To put it simply, technical debt refers to the additional work or rework needed to repair or implement a proper solution. Over time, legacy systems tend to accumulate considerable technical debt, rendering them susceptible to breakdown and tough to maintain.
For additional reading: Understanding Technical Debt
#3) Employing Agile Software Development Method
The Agile methodology for software development focuses on step-by-step development. For teams to successfully extend existing functionalities with each cycle, clean, well-structured, and maintainable code is paramount. Altering code without suitable refactoring can lead to code smells or an augmentation of technical debt.
The diagram below shows this relationship:
Suggested reading: Leading Code Analysis Tools
Understanding Why Testers Should Be Aware of Refactoring
While the topics discussed in this tutorial might seem more applicable to developers, it is equally crucial for testers to comprehend them as well.
So, why highlight this within the Software Testing Help community? Continue reading for the answer.
#1) For Unit Testers/Developers
During code refactoring, modifications to existing classes and the addition of new code can lead to existing unit tests failing. Moreover, older systems may not have unit tests set up, thus necessitating the creation of new tests in most cases.
#2) For Testers
When refactoring a feature (assuming no added functionality), it is typically assumed that most end-user functionality remains unaltered.
- For testers, code refactoring necessitates thorough testing and regression testing. Comprehensive testing covers all existing user flows to ascertain the continual working of all functions as before. Executing a regression test on the entire application (or impacted sections) is critical to ensure that updating a module doesn’t unintentionally disrupt other modules’ functionality.
- User acceptance tests are vital and must be passed before declaring the build ready for deployment.
- Additional tests, like load tests or security tests, might also be needed depending on the requirements of the project.
#3) Automation Test Engineers
Functional and non-functional automation scripts might fail due to code refactoring.
Here are a few reasons:
- If refactoring results in changes to page objects, Selenium automation scripts dependent on those page objects will fail and need revisions.
- Even minor modifications in the structure of the code can make existing automation scripts ineffectual, necessitating updates.
It’s essential to set up functional automation tests once a feature is steady, as testing it prematurely might lead to substantial rework as the feature changes.
Automation test engineers, as developers of automation tests, should strive to create clean and maintainable code. Most popular IDEs, like IntelliJ IDEA and Eclipse, include built-in refactoring menus with frequently used methods for easy referencing.
Below is an image of the refactoring menu in IntelliJ IDEA (Community Edition):
#4) For Test Leads/Quality Assurance (QA) Leads
- Test Leads/QA Leads might need to work with the team, including developers, product analysts, and stakeholders, to ensure meticulous test planning for such projects.
- Understanding the existing functionality and documenting business cases, user flows, and user acceptance tests is important. When testing refactored code, all of these scenarios must be validated, alongside regression testing of affected areas.
- Proactive planning of the test approach and test plans is crucial. If multiple test environments or new test tools are anticipated, requisitions should be made early to prevent delays once testing begins.
- Involved non-project team members or end users in testing can provide valuable insights.
Case Studies
We will now delve into some real-world case studies that I had the opportunity to participate in directly or indirectly.
Case Study #1
Job: Refactor a module by replacing hard-coded values with variables and adding explanations for a new automated technical documentation generation tool.
Challenges: No serious challenges surfaced. The module was relatively new and had documented functional and user acceptance requirements, user flows, and test cases. Unit tests were set up during the initial launch.
Testing Approach: Test cases for the concerned module and the impacted areas were carried out. Any discovered defects were reported and addressed before the release.
Case Study #2
Job: Refactor an existing stored procedure to enhance the scalability of the application.
In this scenario, a stored procedure that was initially designed for an internal application with less than 10 simultaneous sessions. However, the company decided to commercialize it as a Software as a Service (SaaS) application, anticipating initially up to 300 simultaneous sessions.
Load tests were executed by the team, and it was discovered that the system failed with just 25 simultaneous sessions. To fix this, they scrutinized the code and suggested refactoring the core stored procedure to support up to 500 simultaneous sessions without any hiccups.
The identified issues with the stored procedure included multiple subqueries within a single query, heavy joins with views rather than tables, and the use of select * instead of specific columns, among others. These problematic code patterns resulted in excessive data fetching and consequently slowed down the application, leading to crashes.
Obstacles:
#1) Project Manager/Product Analyst
- Requirement Gathering: Due to this stored procedure constituting part of legacy code, no documented requirements were available during the initial module design. Moreover, subsequent iterations lacked a change log, making it challenging to track the module’s evolving business rules and logic.
- Project Schedule: Communicating a precise timetable was difficult due to unclear requirements and code dependencies.
#2) For Developers
- Absence of clear requirements and business rules
- Keeping the code functional during refactoring
- Identifying unknown impacted sections and code dependencies
- Unable to give accurate development time estimations
- Creating new unit tests
#3) For Testers
- Absence of clear requirements and business rules affecting test planning
- Identifying unknown impacted sections requiring regression testing
- Unable to give accurate testing estimates
#4) For Stakeholders
- Insufficient documentation of requirements and user flows, combined with tight deadlines, elevates the risk of failure.
Tactics to Minimize Risks and Tackle Obstacles:
(i) Collaborative Requirement Gathering: The project manager, product analyst, and testers closely collaborated with internal end users to understand and document the primary functionalities and user flows. Developers also added relevant information to the requirements document before the sprint commenced.
(ii) Setting up Alternate Test Environments: Two parallel test environments, Gamma and Phi, were set up, reflecting the before-and-after situation. The Gamma environment housed the original code, while the Phi environment had the refactored stored procedure deployed. This allowed for thorough and exploratory testing by comparing the behavior in both environments, aiding in identifying potential bugs.
(iii) Early Involvement of End Users and Stakeholders in Testing: Early detection of evident issues allowed more time for required fixes to be implemented.
(iv) Defining and Following Exit Criteria: Exit criteria were defined during the planning stage: 80% of user flows were tested, no unresolved critical bugs, and a demonstration and sign-off from stakeholders before release.
(v) Setting a Provisional Release Date: Setting a release date orchestrated the team’s efforts towards a common goal. Based on the scope of the project, the team suggested a three-week sprint rather than the usual two-week sprint to ensure adequate time for project execution.
Final Thoughts
To conclude, code refactoring involves streamlining and cleaning up code without changing its functionality. The refactoring process can be simple, such as adding comments or correct indentation, or complex when dealing with legacy systems.
Code smells, technical debt, and the implementation of an Agile software development method are reasons that may necessitate code refactoring.
For testers, code refactoring equates to thorough testing and regression testing.
Comprehensive testing ensures that all existing user flows keep functioning as intended, and regression testing is crucial to avoid accidental disruption of other modules when performing an upgrade on a specific module.
Test leads and QA leads should work closely with the team and proactively plan testing approaches and plans. Anticipating the need for additional test environments or tools early on prevents delays during the testing phase.
About the Author: This tutorial was penned by Neha B., a seasoned Quality Assurance Manager proficient in leading and managing both in-house and offshore QA teams.
If you have been a part of challenging projects involving code or module/application refactoring, don’t hesitate to share your experiences in the comments section. Your insights can be of help to fellow testers.