BrandGhost
How to Test and Debug a NuGet Package Locally Before Publishing

How to Test and Debug a NuGet Package Locally Before Publishing

07/10/2026

How to Test and Debug a NuGet Package Locally Before Publishing

Knowing how to test nuget package locally is one of the most valuable habits you can build as a .NET library author. You packed your library. The build succeeded. But before pushing to NuGet.org, how do you verify that it actually works end-to-end in a real consumer project? Skipping this step is one of the most common -- and most painful -- mistakes .NET developers make when shipping libraries.

Shipping a broken package is painful. It can break downstream projects, create confusing error messages for consumers, and erode trust in your library. Taking twenty minutes to test nuget package locally and debug nuget package behavior in a controlled environment saves hours of incident response after the fact. A local NuGet feed gives you a fast feedback loop -- pack, install, verify, fix, repeat -- without polluting the public registry with broken versions.

This guide walks through the complete local testing workflow: setting up a local feed, packing to it, consuming the package from a test project, diagnosing cache issues, and inspecting your .nupkg file with NuGet Package Explorer. If you have not yet created your package, check out How to Create a NuGet Package in C# with dotnet pack first -- this guide picks up right where that one leaves off.

Setting Up a Local NuGet Feed

The first step to test nuget package locally is establishing a local NuGet feed. A local NuGet feed is just a folder on disk that NuGet treats as a package source. There is no server, no authentication, and no special tooling required. You drop .nupkg files into the folder and NuGet can resolve packages from it exactly the same way it resolves packages from NuGet.org.

The key is telling NuGet about the folder through a nuget.config file. NuGet searches for nuget.config starting in the current directory and walking up the directory tree, so placing the config file at the root of your solution is the most reliable approach.

Here is a minimal nuget.config that adds a local feed called LocalPackages pointing to a ./local-packages subfolder:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <!-- Keep the default NuGet.org source so other packages still resolve -->
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />

    <!-- Local feed -- a folder on disk next to this config file -->
    <add key="LocalPackages" value="./local-packages" />
  </packageSources>
</configuration>

Place this file at the root of your solution (the same directory that contains your .sln file). The ./local-packages path is relative to the nuget.config file location, so NuGet will look for .nupkg files in <solution-root>/local-packages/.

Create the local-packages folder now -- it can be empty:

mkdir local-packages

That is all the infrastructure you need. No NuGet server, no Docker container, no setup scripts.

If you prefer the command line over editing XML directly, dotnet nuget add source registers the source persistently in the nearest nuget.config (creating the file if it does not exist):

dotnet nuget add source ./local-packages --name LocalPackages

This is equivalent to adding the <add key="LocalPackages" value="./local-packages" /> entry manually and is convenient for quick setups or CI scripts.

Packing to the Local Feed

Once the folder and config are in place, the next step is to build and pack your library directly into it so you can begin to test nuget package locally. The --output flag on dotnet pack controls where the .nupkg ends up (without --output, the .nupkg lands in bin/Release/ by default):

# Build in Release mode and output the .nupkg to ./local-packages
dotnet pack src/MyLibrary/MyLibrary.csproj -c Release --output ./local-packages

After this runs, you should see a file like MyLibrary.1.0.0.nupkg in the local-packages folder. Run dir ./local-packages (or ls ./local-packages on Mac/Linux) to confirm the file is there.

A few things to keep in mind when packing:

  • Version matters. NuGet caches packages by version number. If you re-pack with the same version after fixing a bug, the cache may serve the old version. Always bump the version between test iterations -- or clear the cache (covered below).
  • Use Release mode for final checks. Debug mode is fine for early testing, but your pre-publish smoke test should use -c Release to match what consumers get.
  • The local-packages folder should be gitignored. Add local-packages/ to your .gitignore to avoid committing test artifacts.

Creating a Test Consumer Project

The most realistic way to test nuget package locally and debug nuget package behavior is to consume it from a separate project -- ideally a simple console app that exercises the package's public API the same way a real consumer would.

You have two options for where to put this test consumer:

Option A -- add it to the same solution. This is convenient because the nuget.config at the solution root is automatically picked up by all projects in the solution.

Option B -- create it in a separate directory outside the solution. This more closely mirrors a real consumer's experience, but you need to copy or reference the nuget.config from there.

Option A is the practical starting point for most developers. Add the console app:

# From the solution root
dotnet new console -n MyLibrary.TestConsumer -o tests/MyLibrary.TestConsumer
dotnet sln add tests/MyLibrary.TestConsumer/MyLibrary.TestConsumer.csproj

Keep this test consumer project simple. Its only job is to call your library's public API and verify the results. Think of it as a smoke test driver -- not a formal test suite -- specifically designed to test nuget package locally before you commit to a public release. For structured testing of the library's internal logic, you will want an actual xUnit or NUnit project -- but that is a separate concern from verifying the packaged artifact itself. For a broader look at testing ASP.NET Core Web APIs with WebApplicationFactory, the same philosophy of real-environment testing applies.

Consuming the Local Package

With the test consumer project created and the nuget.config in place, the next step to test nuget package locally is adding the package reference. You can do this with the CLI:

dotnet add tests/MyLibrary.TestConsumer/MyLibrary.TestConsumer.csproj 
  package MyLibrary 
  --version 1.0.0 
  --source ./local-packages

Note: The line continuation above is for bash and zsh. In PowerShell use a backtick ( ``) at the end of each line, and in Windows cmd.exe use^. If in doubt, collapse the command to a single line: dotnet add tests/MyLibrary.TestConsumer/MyLibrary.TestConsumer.csproj package MyLibrary --version 1.0.0 --source ./local-packages`

Or add the PackageReference directly to the .csproj file:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <!-- Pin to the exact version you packed -- no floating ranges during testing -->
    <PackageReference Include="MyLibrary" Version="1.0.0" />
  </ItemGroup>

</Project>

Pin the version exactly during testing. Do not use version ranges like 1.0.* or [1.0.0,) when you want to debug nuget package behavior locally. This includes Version="*" -- the wildcard resolves to the highest version available in any configured source, which can resolve to a cached old version rather than your freshly packed one. Floating ranges make it harder to reason about which version is actually installed. Always pin to the exact version you packed.

Now write some consumer code in Program.cs that exercises your library:

using MyLibrary;

// Exercise the main entry points of your package
var calculator = new Calculator();
var result = calculator.Add(3, 4);

Console.WriteLine($"3 + 4 = {result}");
Console.WriteLine(result == 7 ? "✅ Package works as expected" : "❌ Unexpected result");

Run it:

dotnet run --project tests/MyLibrary.TestConsumer/MyLibrary.TestConsumer.csproj

If restore fails here with "package not found" errors, the most likely causes are: the nuget.config is not in a parent directory of the consumer project, the local-packages folder path is wrong, or the .nupkg filename does not match the package ID and version. Double-check all three.

Troubleshooting: Cache Issues When You Test NuGet Package Locally

NuGet aggressively caches packages to avoid redundant downloads. This is great for performance in production but frustrating when you are iterating locally. The most common symptom: you packed a new version, but dotnet restore still seems to be using the old one. This is the single most common pain point when you try to debug nuget package behavior across multiple iterations.

The NuGet cache lives here by default:

  • Windows: %userprofile%.nugetpackages
  • Linux / macOS: ~/.nuget/packages/

To see all cache locations on your machine:

# List all NuGet cache locations
dotnet nuget locals all --list

This outputs something like:

http-cache: C:UsersyournameAppDataLocalNuGetv3-cache
global-packages: C:Usersyourname.nugetpackages
temp: C:UsersyournameAppDataLocalTempNuGetScratch
plugins-cache: C:UsersyournameAppDataLocalNuGetplugins-cache

To clear caches when you need a fresh restore:

# Clear ONLY the global packages cache (safer -- targets only resolved packages)
dotnet nuget locals global-packages --clear

# Clear ALL caches including http-cache, temp, and plugins
dotnet nuget locals all --clear

The global packages cache is the one causing stale-version issues in most local testing scenarios. Clearing just that cache is usually enough, and it is safer than wiping everything including the HTTP cache (which holds downloaded index metadata).

If you want to bypass the cache for a single restore without clearing it permanently, use the --no-cache flag:

dotnet restore --no-cache

For the most stubborn cases where the restore still resolves the wrong version, add --force-evaluate to force NuGet to re-evaluate all version constraints from scratch:

dotnet restore --force-evaluate

One workflow that eliminates most cache confusion when you test nuget package locally: bump the patch version on every test iteration. 1.0.0 becomes 1.0.1, then 1.0.2, and so on. Since each version is unique in the cache, there is no stale entry to fight. You can reset to a clean version number before the final publish.

Inspecting Your Package with NuGet Package Explorer

Before you push to NuGet.org, it is worth opening the .nupkg file itself and verifying its contents. This is one of the most underused techniques to test nuget package locally -- visual inspection of the packaged artifact before it ever ships. The structure inside the archive determines what consumers get -- and it is easy for a misconfigured .csproj to silently omit files, include debug symbols when you did not intend to, or produce an incorrect .nuspec manifest.

NuGet Package Explorer is a free Windows GUI tool for doing exactly this. It is available from the Microsoft Store or as a direct download from GitHub.

Open your .nupkg file in NuGet Package Explorer and verify:

  • The .nuspec tab shows the correct package ID, version, description, author, and license. These are what consumers see on NuGet.org.
  • The lib/ folder contains your compiled assembly (e.g., lib/net8.0/MyLibrary.dll). If this folder is missing or empty, consumers will not get a usable assembly.
  • Target framework folders match your intended targets. A package targeting net8.0 and netstandard2.0 should have both lib/net8.0/ and lib/netstandard2.0/.
  • The build/ folder (if present) contains any MSBuild props/targets files you are distributing.
  • The content/ folder (if present) contains any content files your package installs into the consuming project.
  • No debug artifacts you did not intend to include. PDB files add to the package size and expose your source paths -- unless you are intentionally distributing embedded debug symbols. For enabling consumers to step through your source code in a debugger, the modern approach is SourceLink combined with a symbol package (.snupkg), not embedded PDBs in the main .nupkg.

You can also inspect a .nupkg manually since it is just a ZIP archive -- rename it to .zip and open it in any archive viewer. But NuGet Package Explorer renders the .nuspec and folder structure in a much friendlier way, especially when checking multi-targeted packages. If your library makes use of custom attributes in C# that consumers read via reflection, checking the .nuspec manifest is particularly important to confirm those metadata values are set correctly.

Testing Multi-Targeted Packages

If your library targets multiple frameworks -- for example, net8.0 and netstandard2.0 -- you need to test nuget package locally against each target to verify correct resolution. Change the TargetFramework in your test consumer's .csproj and re-run the restore and build:

<!-- Test with each framework your package targets -->
<TargetFramework>net8.0</TargetFramework>
<!-- Then try: <TargetFramework>net6.0</TargetFramework> -->
<!-- Then try: <TargetFramework>netstandard2.0</TargetFramework> -->

After changing the target framework, clear the global packages cache and restore again so NuGet picks the right lib/ subfolder from your package.

Things to check when testing multi-targeted packages:

  • The correct assembly is resolved. NuGet uses the TFM compatibility algorithm to select the best-matching lib/ folder. A net6.0 consumer should get the net6.0 assembly, not the netstandard2.0 fallback, if both exist.
  • APIs are consistent across targets. If you use conditional compilation (#if NET8_0_OR_GREATER), verify the correct code paths are active under each framework.
  • No missing dependencies. A package that works on net8.0 may fail on netstandard2.0 if it depends on APIs not available in that target. Test the consumer project under each target to catch this early.

NuGet Package Explorer is helpful here too -- expand the lib/ node and confirm each target framework folder contains the expected assembly.

Final Checks Before Publishing

Once the local test consumer runs cleanly and NuGet Package Explorer confirms the package structure looks right, run through these final checks before pushing to NuGet.org. These are the same checks you want to verify every time you test nuget package locally -- not just on the first iteration.

Version is correct. The version in your .csproj (<Version>) should follow SemVer. If this is a patch fix, bump the patch. Breaking changes require a major version bump. Do not leave a test-iteration version like 1.0.7 as the published version if you intended to ship 1.1.0.

Package metadata is complete. Open NuGet Package Explorer one more time and verify the description, author, license, and repository URL are all set. Packages with missing metadata are harder for consumers to evaluate.

The README is included. If you have a README.md referenced from your .csproj with <PackageReadmeFile>, confirm it appears in the package. NuGet.org renders this on the package page.

Symbol package is intentional. A .snupkg (symbol package) enables step-through debugging for consumers. If you want to support that, generate it with --include-symbols. If you do not, confirm no symbol package was accidentally generated.

Test on a clean machine or clean cache. The most reliable final smoke test is to clear all local caches and run the consumer project one more time. This proves the package is fully self-contained and does not accidentally rely on anything in your local environment. It is the closest simulation you can get to how a brand-new consumer will experience your package.

When all of these pass, you are ready to publish. For the step-by-step publishing process, see How to Publish a NuGet Package to NuGet.org -- that guide covers API keys, the dotnet nuget push command, and post-publish verification.

Wrapping Up

Taking the time to test nuget package locally is a straightforward process once the workflow is in place. Set up a nuget.config with a local feed folder, pack to it with dotnet pack --output, consume it from a test project with a pinned version, and use dotnet nuget locals to stay on top of cache issues. Add NuGet Package Explorer to your toolkit for visual inspection of the .nupkg contents before every publish. Each step to debug nuget package behavior at this stage costs far less than discovering issues after your package is live.

The extra twenty minutes this workflow takes to test nuget package locally before publishing saves hours of fire-fighting after a bad release reaches consumers. And for developers building multi-targeted packages, the local testing loop is practically mandatory -- the framework resolution rules are subtle enough that "it compiled" is never enough evidence that the package works.

For the full picture of what goes into a well-crafted package from start to finish, the Complete Guide to Creating NuGet Packages in .NET covers everything from project configuration to versioning strategy in one place.


Frequently Asked Questions

What is a local NuGet feed and how does it work?

A local NuGet feed is a folder on disk that NuGet treats as a package source. When you want to test nuget package locally, you drop .nupkg files into the folder, add the folder as a packageSources entry in nuget.config, and NuGet can resolve and install packages from it exactly like it would from NuGet.org. No server or network connection is required.

Why does dotnet restore still use the old version after I re-packed?

NuGet caches resolved packages in %userprofile%.nugetpackages on Windows. If you re-pack with the same version number when you want to debug nuget package behavior, the cache may serve the old binary. Run dotnet nuget locals global-packages --clear to remove the cached version, or bump the version number on each test iteration to avoid the conflict entirely.

What is the difference between dotnet nuget locals all --clear and dotnet nuget locals global-packages --clear?

dotnet nuget locals global-packages --clear clears only the resolved package cache -- the folder that holds the actual .nupkg files NuGet has downloaded and extracted. dotnet nuget locals all --clear clears everything, including the HTTP response cache and temp files. For most debug nuget package scenarios, clearing only global-packages is sufficient and safer.

Can I test nuget package locally without modifying nuget.config?

Yes. You can pass the --source flag directly to dotnet add package or dotnet restore to specify the local folder path at the command line without a nuget.config change. However, using nuget.config is more reliable because it ensures every restore in the project picks up the local feed automatically, including implicit restores triggered by dotnet build. Most developers who test nuget package locally regularly find the config file approach easier to maintain.

Does NuGet Package Explorer work on macOS and Linux?

NuGet Package Explorer was originally a Windows-only application, but cross-platform builds are available from the GitHub releases page. On macOS and Linux you can also inspect a .nupkg by renaming it to .zip and extracting it with any archive tool. The .nuspec file inside is plain XML, and the lib/ folder structure is directly visible in the extracted contents.

How do I test a multi-targeted package locally?

Change the <TargetFramework> in your test consumer project to each framework your package targets (e.g., net8.0, net6.0, netstandard2.0). Clear the global packages cache between each change, restore, and build. This forces NuGet to re-resolve the package against the new target and proves the correct lib/ subfolder is selected. This is especially important when you test nuget package locally for libraries that expose different APIs under different target frameworks.

Should I add local-packages to .gitignore?

Yes. The local-packages/ folder contains generated .nupkg artifacts that should not be committed to source control. Add local-packages/ to your .gitignore the same way you would exclude bin/ and obj/ folders. The nuget.config file itself, however, should be committed -- it is part of your project's configuration.

How to Create a NuGet Package in C# with dotnet pack

Step-by-step guide to creating a NuGet package in C# using dotnet pack. Learn how to configure your .csproj, set package properties, and generate a .nupkg file.

How to Publish a NuGet Package to NuGet.org

Learn how to publish NuGet package files to NuGet.org using dotnet nuget push. Covers API keys, nuget.config setup, the validation queue, and unlisting.

The Complete Guide to Creating NuGet Packages in .NET

Learn how to create, version, and publish NuGet packages in .NET. This complete guide covers dotnet pack, metadata, versioning, publishing, CI/CD, and more.

An error has occurred. This application may no longer respond until reloaded. Reload