A green local test run is evidence about one thing: the tests and build context that actually executed passed on your machine. It does not show that the repository’s full project or dependency graph is complete, that every module and configuration was resolved, or that your continuous integration (CI) system resolves dependencies the same way. When tests pass locally but a dependency relationship is missing or inconsistent elsewhere, the two checks have usually covered different ground.
This article explains the difference between running tests and validating the project graph, lists the places where static dependency information and the real build can disagree, and gives a diagnostic sequence you can apply to a specific repository.
As an Amazon Associate I earn from qualifying purchases.
What “project graph” means here
The term is used loosely. In this article, a project graph is the set of build and dependency relationships a build tool resolves: which projects depend on which, which components and variants are selected, and which direct and transitive dependencies end up on each classpath or in each package. It is not the same as an architecture diagram or a module-dependency diagram drawn for documentation, although those can be used to validate the same code.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Build tools build this graph from configuration files and dependency declarations. Gradle, for example, describes a resolved graph as relationships among components and variants, including direct and transitive dependencies, and its dependencies task can display part of that graph. The authoritative description is in the Gradle User Manual’s Graph Resolution page, which reports version 9.8.0 at the time of writing.
Test execution and graph validation answer different questions
A test run answers: did this selected code, under this selected task, behave as the tests expect? A graph validation answers: are the relationships the build will use complete, resolvable and consistent with the rules the repository has set? A test can exercise a module that never touches a broken dependency, and a graph check can flag a problem in a module no test imports.
| Check | What it establishes | What it does not establish |
|---|---|---|
| Local test run (one project or one test target) | The selected tests passed in your local environment with the tool versions and variables you have. | That other projects, configurations or the whole solution were built or tested, or that CI resolves the same versions. |
| Full build task on the whole solution, with the configuration CI uses | Everything included in that task compiled and resolved under that configuration. | That static manifests or submitted graphs show every relationship, or that build-time-only dependencies match what production resolves. |
| Static dependency graph (manifests and lockfiles) | The dependencies that supported manifests and lockfiles declare, including direct and transitive ones. | Dependencies that only appear at build time, values set by environment variables, or files that were never declared. |
| Build-resolved graph (generated during the actual build) | The dependencies the build tool resolved in that build environment. | Anything outside the scope of that build, such as a project or configuration the job did not run. |
| Architecture or layer validation | Whether code respects the dependency rules written in the validation diagram, within the analysed scope. | Whether every file was analysed; live validation may cover only edited files unless full solution analysis is enabled. |
The practical consequence is that “the tests passed” is a statement about a subset. Before reading a green result as a clean graph, state which command, which target and which configuration produced it.
Why the graph you see can differ from the real build
Static dependency detection reads what a repository exposes in its manifests and lockfiles. The build, however, resolves what those files, the environment and the build scripts produce together. Several gaps follow from that difference.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Variables that only the build environment can resolve
GitHub’s documentation for the dependency graph notes that some values in manifests may depend on the build environment. A version written as a placeholder or a property is not always resolvable from the file alone. If your local shell exports a property that CI does not set, the local resolution can succeed while CI resolves a different version or fails. The GitHub Docs troubleshooting page for the dependency graph describes these environment-dependent cases and the processing limits that apply to them.
Dependencies copied or generated into the repository
Loose dependency files copied into a repository are not automatically included in the graph. A vendored JAR, a hand-copied library or a generated artefact can be on your classpath without appearing in any manifest the graph reads. A local test that imports the vendored code will pass; a graph-based check will not see the relationship at all unless it is declared or submitted.
Build-time dependencies that need separate submission
Some dependencies exist only during the build, such as code generators, plugins or tooling resolved by the build script. GitHub documents that build-time dependencies may need to be submitted through an API or through an automatic workflow. If nobody submits them, the static graph is incomplete by design, even when the build itself is correct. Gradle’s dependency-submission action documentation describes one route for sending build-resolved data.
Processing limits and analysis scope
Graph generators apply limits. GitHub documents limits on manifest size and on the number of manifests processed. Microsoft’s layer-diagram validation can analyse only edited files in live validation unless full solution analysis is enabled. A report that looks complete may therefore omit files or relationships for reasons unrelated to your code. Read the stated scope before treating the report as an inventory.
Generated graph data is only as complete as its job
GitLab warns that a dependency graph generated from SBOM data may not reflect dependencies resolved in the actual build environment. Its guidance is to generate graph data inside a controlled build job where that is possible, so the data reflects the same resolution the build uses. The GitLab Docs page on dependency scanning by using SBOM sets out that caveat.
None of these mechanisms proves that a specific local/CI mismatch has one of these causes. They are the documented ways a local test can pass while a graph-based check, or a different CI resolution, finds a missing or inconsistent relationship.
Rank #4
Lockfiles improve repeatability but do not prove coverage
Lockfiles record exact resolved versions, and GitHub’s explanation of how the dependency graph recognises dependencies makes the practical point: lockfiles keep contributor versions consistent, which makes it easier to test and debug. That benefit is real. If two developers see different versions of a transitive dependency, a lockfile narrows the gap.
A lockfile, however, records the dependencies that were resolved when it was written. It does not show whether every project in the repository was built against it, whether a configuration was excluded from the run, or whether a dependency was added after the lockfile was last updated. NuGet illustrates the split: the generated obj/project.assets.json file manages the overall dependency graph used by a project, according to Microsoft Learn’s description of NuGet. That file describes one project’s resolved graph; it is not a claim about the whole solution.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Treat a lockfile as evidence about the versions that were pinned, not as proof that the full graph was exercised.
Best Value
Why CI can disagree with your machine
When local tests pass and CI fails, the difference is usually in one of four places: the scope of the command, the tool and runtime versions, the environment variables, or the task selection. The graph adds a fifth: where dependency data comes from. The comparison below is the basis for the diagnostic sequence that follows.
| Dimension | Local run | CI run | What to compare |
|---|---|---|---|
| Scope | One module or the whole solution, depending on the command you typed | Often the full pipeline, sometimes a matrix of projects | Exact task and target names, and whether a filter excludes projects |
| Source of graph data | Usually the build tool’s own resolution | May add a static manifest graph or a submitted snapshot | Whether CI submits build-resolved data or relies on static files |
| Reproducibility | Versions may come from a local cache or an unpinned range | Versions may come from a clean resolution | Lockfile presence, cache state and dynamic version ranges |
| Validation stage | Local command only | Build step, plus any architecture or graph validation step | Which checks run in CI but not locally |
| Documented limits | Your tool’s configured analysis scope | Platform limits on manifest size and count, and analysis scope set in the pipeline | The stated limits on each side |
A diagnostic sequence for a missing or inconsistent dependency
Work through these steps in order. Each one narrows the cause before you change code.
- Name the command that passed. Record the exact invocation, such as
./gradlew :app:testordotnet testagainst a named project. Note whether it ran one test project, one configuration, or the whole solution. - Run the intended full build. Execute the full build and any integration or architecture-validation tasks that CI requires, including those with separate validation stages. A green result only counts for the tasks that ran.
- Inspect the resolved graph. For Gradle, run
./gradlew :app:dependencies --configuration runtimeClasspathfor each project and configuration in question, replacing the names with yours. Check the output for the dependency that is missing or at an unexpected version. Other build tools have equivalent commands. - Compare declared dependencies with the real resolution. Set the lockfile, the manifest and the resolved graph side by side. Look for property placeholders, dependencies copied into the repository, and build-script dependencies that never appear in a manifest.
- Check environment variables and the build context. Compare the variables set locally with those set in CI. A property defined only on your machine is a common source of divergence.
- Make graph generation run in the build. Where possible, generate graph data from the same build job that compiles the code. GitHub’s dependency submission REST API accepts build-resolved dependency snapshots, and GitLab recommends generating graph data within a controlled build job when appropriate.
- Check validation scope and processing limits. Confirm whether the validation analysed the files you changed or the whole solution, and whether the graph report hit a size or count limit. A report that omits a file is not evidence that the file has no problem.
- Reproduce the CI environment before changing code. Match tool versions, runtime versions, configuration files and environment variables, and rerun the same task selection. Only after the environments match should you change the dependency or the code.
Choosing a stronger check
If the local test is the only check you run, add one of these before trusting a green result for dependency changes:
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minute- A full build of the solution or the project set that CI builds, using the same task names.
- A build-resolved dependency snapshot submitted from the build job, rather than a static manifest alone.
- An architecture or layer validation run with full solution analysis enabled, if your team uses dependency diagrams.
- A lockfile check in CI, so that version changes appear as explicit diffs.
Each check covers a different part of the graph. Use them together and state, in the pull request or the build summary, which scope each one covered.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




