How TDD and GTest Transform Legacy Navigation Systems: A Practical Guide
This article explores how Test‑Driven Development and Google Test can improve quality and enable incremental refactoring of a complex, legacy online navigation system by addressing the shortcomings of traditional diff‑based testing, introducing unit‑testing fundamentals, and sharing practical implementation details and lessons learned.
Business Background
Gaode online navigation service is a legacy system with massive code, strong business characteristics, and years of accumulation, leading to many unreasonable code sections and increasing demands on performance, algorithms, and architecture, making rapid refactoring a major engineering challenge.
Problems with Existing Quality Assurance Methods
1. Issues with current testing methods
Invalid diff problem: multiple downstream services (routing engine, search, incident handling, traffic) cause many irrelevant diffs.
Long execution time: a large number of cases can take minutes, limiting frequent testing.
Difficult debugging: request‑level diffs obscure the root cause, making investigation hard.
2. Industry mainstream practices
Companies such as ThoughtWorks and Google adopt Test‑Driven Development (TDD) for agile development, using unit tests to ensure quality during development and refactoring, which has become a best practice.
Introduction to Unit Testing
1. What is unit testing?
Unit testing validates the correctness of a module, function, or class. It is lightweight, runs in seconds, and is ideal for the "small step" quality assurance needed in incremental refactoring.
2. Unit testing frameworks
The xUnit family provides frameworks for many languages (CppUnit, JUnit, NUnit, etc.). GTest, developed by Google, offers advanced features such as death tests and mocks.
3. Relationship between unit testing, refactoring, TDD, and agile
TDD emphasizes writing tests before code, turning each test case into a specification. Accumulated test cases effectively safeguard development and subsequent refactoring, making them essential for agile practices.
Bus Service Unit‑Test Practice
1. Integrating GTest
Clone the googletest repository (https://github.com/google/googletest) and link libgtest to the project.
Execute tests with the following code:
int RCUnitTest::Excute()
{
int argc = 2;
char* argv[] = {const_cast<char>(""), const_cast<char>("--gtest_output=\"xml:./testAll.xml\"")};
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}Control test execution via compile‑time flags or configuration switches to avoid affecting production builds.
2. Writing test cases
Derive a class from testing::Test and use the TEST_F macro to add test functions. Example:
class DateTimeUtilTest : public ::testing::Test
{
protected:
void SetUp() {}
void TearDown() {}
};
TEST_F(DateTimeUtilTest, TestAddSeconds_leap)
{
// Leap year test 2020‑02‑28
tm tt;
tt.tm_year = 2020 - 1900;
tt.tm_mon = 1;
tt.tm_mday = 28;
tt.tm_hour = 23;
tt.tm_min = 59;
tt.tm_sec = 50;
DateTimeUtil::AddSeconds(tt, 30);
EXPECT_TRUE(tt.tm_sec == 20);
EXPECT_TRUE(tt.tm_min == 0);
EXPECT_TRUE(tt.tm_hour == 0);
EXPECT_TRUE(tt.tm_mday == 29);
EXPECT_TRUE(tt.tm_mon == 1);
// Non‑leap year test 2019‑02‑28
tm tt1;
tt1.tm_year = 2019 - 1900;
tt1.tm_mon = 1;
tt1.tm_mday = 28;
tt1.tm_hour = 23;
tt1.tm_min = 59;
tt1.tm_sec = 50;
DateTimeUtil::AddSeconds(tt1, 30);
EXPECT_TRUE(tt1.tm_sec == 20);
EXPECT_TRUE(tt1.tm_min == 0);
EXPECT_TRUE(tt1.tm_hour == 0);
EXPECT_TRUE(tt1.tm_mday == 1);
EXPECT_TRUE(tt1.tm_mon == 2);
}Test execution results are shown below:
To date, 23 modules covering core functions such as station search, routing, ETA, fare, and risk shutdown have been covered, enabling continuous incremental refactoring with low defect introduction.
Challenges and Difficulties
Data dependency issues
The navigation engine heavily depends on data; constructing realistic mock data is costly, and data changes can cause test failures. Simple fake data can be used where feasible; otherwise, real data is acceptable as long as tests pass before and after refactoring.
Common Misconceptions
"No time for tests" – TDD’s test‑first approach clarifies requirements and does not add extra cost.
"Historical code is too much" – Start by adding tests for the code you modify; tests help understand legacy code and enable safe refactoring.
Incremental Refactoring of Legacy Systems
Large, historic codebases cannot be rewritten easily; instead, set major milestones and apply gradual refactoring, using unit tests and TDD as the safety net.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Alibaba Cloud Developer
Alibaba's official tech channel, featuring all of its technology innovations.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
