![](/style/images/good.png)
![](/style/images/bad.png)
Integration tests for .NET CLI tools
source link: https://blog.cloud-eng.nl/2023/01/22/dotnet-cli-integration-tests/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
TABLE OF CONTENT
Iβve spent Saturday evening working on a small project. Iβve built a .NET clone of tree
utitly. You know, the one that renders a nice tree view for a given directory.
The project is available on GitHub under the dtree name. Itβs a .NET global tool that you can install with the following command:
dotnet tool install -g dtree
The tool is pretty simple. It takes a path to a directory as an argument and renders a tree view for it. Here is an example:
dtree -a
π .
βββ π .devcontainer
β βββ devcontainer.json
βββ π .github
β βββ π workflows
β βββ build-and-publish.yml
βββ π src
β βββ dtree.csproj
β βββ Program.cs
β βββ TreeNode.cs
βββ .gitignore
βββ LICENSE
βββ README.md
The codebase is quite hacky. Itβs a single file with top level statements. Not much abstractions over the System.Console
and System.IO.Directory
libraries. However, I wanted to make sure that it works as expected. So, Iβve added a few integration tests to the project.
I wanted to build tests that run the compiled tool and compare the output with the expected result. One would expect to have something similar to ASP.NET WebApplicationFactory. However, there is no such thing for .NET CLI tools. So, I had to come up with a solution.
Iβve created a new project, that wonβt reference the dtree
project directly. Instead, it will use the CliRunner
wrapper around Process
object that will execute dotnet run
command to compile and run the dtree
tool.
using System.Diagnostics;
namespace dtree.IntegrationTests;
public class CliRunner
{
private readonly string cliProjectPath;
private readonly Process process;
public CliRunner(string cliProjectPath)
{
this.cliProjectPath = cliProjectPath;
process = new Process();
process.StartInfo.FileName = "dotnet";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
}
public (int exitCode, string output) Run(string args)
{
process.StartInfo.Arguments = $"run --project {cliProjectPath} -- {args}";
process.Start();
var output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
return (process.ExitCode, output);
}
}
And this wrapper can be used in tests.
using Xunit.Abstractions;
namespace dtree.IntegrationTests;
public class DTreeTests
{
private readonly string cliProjectPath;
private readonly CliRunner runner;
public DTreeTests(ITestOutputHelper output)
{
cliProjectPath = Path.Combine(Directory.GetCurrentDirectory(), "../../../../src/dtree.csproj");
output.WriteLine($"{cliProjectPath}");
runner = new CliRunner(cliProjectPath);
}
[Theory]
[InlineData("-d")]
[InlineData("--max-depth")]
public void Negative_Depth_Results_As_Error(string key)
{
var (exitCode, output) = runner.Run($"{key} -1");
Assert.Equal($"π± Max depth must be greater than 0{Environment.NewLine}", output);
Assert.Equal(1, exitCode);
}
[Theory]
[InlineData("-a")]
[InlineData("--all")]
public void All_Files_Should_Be_Included(string key)
{
var (exitCode, output) = runner.Run($"{key} -p ../../../../");
Assert.Contains("π .devcontainer", output);
Assert.Contains("π .github", output);
Assert.Contains("π src", output);
Assert.Contains(".gitignore", output);
Assert.Contains("LICENSE", output);
Assert.Contains("README.md", output);
Assert.Equal(0, exitCode);
}
}
This way I can run the tests in CI
steps:
- uses: actions/checkout@v3
name: π₯ Checkout
- name: π§ͺ Execute Unit Tests
run: dotnet test ./tests
And be sure to publish the package to NuGet if everything goes well.
Give it a try π
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK