Backend Development 9 min read

Applying Test‑Driven Development and GTest for Unit Testing in Gaode’s Navigation Service

Gaode’s navigation service adopted test‑driven development using Google’s GTest to replace slow diff‑based checks with fast, lightweight unit tests, enabling incremental refactoring of legacy code, improving quality, handling data‑dependency challenges, and demonstrating that writing tests first clarifies requirements while adding minimal overhead.

Amap Tech
Amap Tech
Amap Tech
Applying Test‑Driven Development and GTest for Unit Testing in Gaode’s Navigation Service

TDD (Test‑Driven Development) emphasizes a test‑first approach, using unit test cases to ensure quality when developing or refactoring complex legacy systems.

Business background : Gaode online navigation services have accumulated a large amount of legacy code with strong business characteristics. Continuous performance, algorithm, and architecture demands conflict with rapid evolution of existing code, making quality‑assured fast refactoring a primary engineering challenge.

Problems with existing diff‑based testing : The conventional method of batch request diff comparison suffers from invalid diffs, long execution time (often minutes), and difficulty in pinpointing issues because diffs are at the request level.

Industry practice : Companies such as ThoughtWorks and Google adopt TDD with unit tests as a mainstream best practice for agile development.

Unit testing overview : Unit testing validates the correctness of a module, function, or class. It is lightweight, runs in seconds, and is ideal for incremental refactoring where each small step is quality‑guaranteed.

Unit testing frameworks : The xUnit family provides frameworks for many languages (CppUnit, JUnit, NUnit, etc.). For C++ we chose Google’s GTest, which offers advanced features like death tests and mocks.

GTest integration : Adding the googletest library to the project and linking with libgtest is sufficient. Example code to drive test execution:

int RCUnitTest::Excute()
{
  int argc = 2;
  char* argv[] = {const_cast
(""), const_cast
("--gtest_output=\"xml:./testAll.xml\"")};
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

Test case example : Using TEST_F to write a test for DateTimeUtil :

class DateTimeUtilTest : public ::testing::Test
{
protected:
    virtual void SetUp() {}
    virtual 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);
};

The execution results show that the navigation engine has accumulated 23 module test cases covering core functions such as station search, routing, ETA, fare, and risk shutdown. Continuous test accumulation enables incremental refactoring while keeping defect introduction low.

Challenges : Heavy data dependencies make mocking expensive; simple fake data can be constructed when possible, otherwise real data is used with the understanding that data changes may cause test failures, which is acceptable as long as tests pass before and after each refactor.

Common misconceptions : Some believe there is no time to write unit tests. In reality, TDD’s test‑first step clarifies requirements and incurs minimal extra cost while providing long‑term quality benefits.

Adding tests to legacy code : Start by writing tests for the code being modified, gradually building a test suite that clarifies inputs/outputs and safeguards refactoring.

Conclusion : Unit testing and TDD are essential tools for progressive, safe refactoring of large, complex backend systems.

Backend Developmentsoftware qualityC++unit testingGTestTDD
Amap Tech
Written by

Amap Tech

Official Amap technology account showcasing all of Amap's technical innovations.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.