When you change one file in a software project, the impact of that change is not always limited to that file.
A function may be imported by several modules. Those modules may be used elsewhere in the application. A seemingly small change can therefore affect code that is not immediately obvious.
This creates a practical problem for developers: How do you identify the parts of a codebase that may be affected by a change, and which tests should you run first?
I built TestScope to explore a graph-based approach to answering that question.
Project links
- Live demo: https://test-scope.vercel.app/
- GitHub repository: https://github.com/Pikanshi1/TestScope
The problem: Understanding the impact of a code change
Consider a Python repository with modules that depend on one another.
If auth.py is changed, the affected code might include a service that imports it, another module that uses that service, and tests associated with those components.
In a larger repository, following these relationships manually becomes difficult. Running every test may also take time, while running only the tests associated with the changed file may miss relevant dependencies.
The challenge is to move from a list of changed files to a better understanding of their relationships with the rest of the codebase.
The idea behind TestScope
TestScope analyzes a Python repository and represents module relationships as a graph using FalkorDB.
Instead of treating files as isolated items, it models them as connected entities. This makes it possible to traverse dependency relationships and identify modules and tests that may be relevant to a change.
The project focuses on three core capabilities:
- Dependency visualization: Representing modules and their relationships as a graph.
- Impact analysis: Exploring connected modules to understand the potential reach of a change.
- Test prioritization: Ranking relevant tests using dependency relationships and graph distance.
The goal is not to replace a test runner or guarantee that every affected test will be identified. It is to provide developers with more useful information when deciding where to start.
Why use a graph database?
Traditional file searches are useful for locating code, but dependency analysis involves relationships between multiple entities.
A graph represents these relationships directly.
In TestScope, module nodes represent Python files, while edges represent relationships between modules. Test modules can also be identified so that the analysis can connect changed code with relevant tests.
This is where FalkorDB fits into the architecture.
FalkorDB provides a graph database for storing and querying connected data. Its graph-query capabilities allow TestScope to explore relationships without manually walking through every connection in application code.
A simplified conceptual representation looks like this:
Changed Module
|
v
Dependent Module
|
v
Application Service
|
v
Related Test Module
This diagram illustrates the idea rather than a specific dependency chain from a particular repository.
How TestScope works
1. Prepare a Python repository
The workflow begins when a user provides a repository URL.
TestScope prepares the repository for analysis and builds the graph representation used by its analysis functions.
The graph captures module paths, test-module information, and relationships between modules.
2. Represent dependencies in FalkorDB
Once the repository has been prepared, TestScope uses its graph-query layer to access the graph.
For example, a Cypher-style query can retrieve module nodes and their test status:
MATCH (m:Module)
RETURN m.path, m.is_test
ORDER BY m.path
Relationships can be retrieved separately:
MATCH (a:Module)-[r]->(b:Module)
RETURN a.path, type(r), b.path
These queries expose the nodes and edges needed to present the dependency graph.
3. Trace the potential impact of a change
When a developer selects changed Python files, TestScope explores the graph to identify related modules.
The current analysis uses a maximum traversal depth of three hops for its blast-radius and test-ranking operations.
Why limit the depth?
A traversal limit gives the analysis a defined scope. It helps focus on nearby relationships rather than following every possible connection through the entire repository.
However, a three-hop limit is a configuration choice, not a guarantee that every affected module will be found. Some indirect dependencies may exist beyond that range.
4. Rank relevant tests
Finding connected modules is only part of the problem. Developers also need a practical way to decide which tests to inspect first.
TestScope includes a test-ranking function that considers graph relationships within the configured traversal depth and returns a limited list of candidates.
The current analysis requests up to ten ranked tests.
This is a form of dependency-based prioritization: tests connected more closely to the changed code can become useful candidates for early investigation.
Importantly, this ranking should be interpreted as guidance, not proof that a test will fail or that a test outside the list is unnecessary. Dependency proximity is a useful signal, but it does not capture every runtime behavior or semantic relationship.
The architecture
At a high level, the application has three main parts:
- Frontend: A React and TypeScript dashboard for interacting with the analysis workflow.
- Backend: A FastAPI service that prepares repositories and exposes graph and analysis endpoints.
- Graph storage: FalkorDB, which stores and queries module relationships.
The backend exposes endpoints for repository preparation, retrieving tests, reading graph data, and analyzing changed files.
The analysis layer separates graph queries from the API routes, making the dependency and ranking logic easier to work with independently.
What I learned from building TestScope
One important takeaway is that dependency analysis is not just a file-search problem. The relationships between files are often as important as the files themselves.
Representing those relationships as a graph creates a natural foundation for questions such as:
- Which modules are connected to this changed file?
- How far does the potential impact extend?
- Which tests are reasonable candidates to run or inspect first?
- How can a developer explore the structure of an unfamiliar repository?
Another lesson is the importance of communicating uncertainty. A dependency graph can reveal useful structural relationships, but it cannot automatically prove that a particular change is safe. Static relationships, runtime behavior, and actual test outcomes are different kinds of evidence.
Current limitations and future directions
TestScope is a project exploring graph-based impact analysis, not a replacement for comprehensive testing.
Its current approach is constrained by the relationships represented in the graph and the configured traversal depth. A graph-based ranking can also miss behavioral dependencies that are not captured by the analyzed relationships.
Future improvements could include evaluating ranking quality against actual test outcomes, supporting more dependency types, and comparing the suggested test order with a full test-suite run.
These improvements would need to be validated against real repositories before making claims about accuracy or time savings.
Try TestScope
You can explore the project here:
- Live demo: https://test-scope.vercel.app/
- Source code: https://github.com/Pikanshi1/TestScope
If you work with Python repositories, I would be interested in hearing which parts of dependency analysis and test selection take the most time in your workflow.
TestScope started from a straightforward question: when code changes, how can we make the relationships between that change and the tests we should consider easier to understand?
Graph-based analysis is one approach I am exploring to answer it.
Top comments (0)