PlayNicely.Executor 1.3.2-beta-550

This is a prerelease version of PlayNicely.Executor.
dotnet add package PlayNicely.Executor --version 1.3.2-beta-550                
NuGet\Install-Package PlayNicely.Executor -Version 1.3.2-beta-550                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PlayNicely.Executor" Version="1.3.2-beta-550" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add PlayNicely.Executor --version 1.3.2-beta-550                
#r "nuget: PlayNicely.Executor, 1.3.2-beta-550"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install PlayNicely.Executor as a Cake Addin
#addin nuget:?package=PlayNicely.Executor&version=1.3.2-beta-550&prerelease

// Install PlayNicely.Executor as a Cake Tool
#tool nuget:?package=PlayNicely.Executor&version=1.3.2-beta-550&prerelease                

Play Nicely - Executor

The Play Nicely Executor project extends the PlayNicely.Projects package to support execution of programs, against test case projects. The execution occurs within a pre-defined, side-effect free, environment. It then collects program output so that test assertions can be made.

The Executor package, provides a fluent interface to define an ITestEnvironment through a builder, with which, some programs can be defined as uninstalled, other programs as required. A test environment can also define what NuGet package sources are available and what packages are mapped to those sources, so that BDD projects can test new functionality. If a test case project is specified, the file system of that project will be 'materialized' and set as the working directory for the executed process.

How It Works

The main types are the ITestEnvironment, TestEnvironmentBuilder, and ITestEnvironmentRunner. An ITestEnvironment ensures a side-effect free environment, within which, any testing can run. You create an ITestEnvironment using the fluent interface of TestEnvironmentBuilder. With this class you define the desired working directory and execution context for your ITestEnvironmentRunner.

Tests run using the ITestEnvironmentRunner, this package defines a basic ProcessRunner which runs an executable with arguments, much like a command line, and returns a basic ExecutionResult. The base result includes whether the command succeeded and the stdout and stderr streams. The DotNetRunner class, defined in PlayNicely.Executor.DotNet executes a dotnet subcommand, with arguments, and returns a ExecutionResult<DotNetExecutionContext>. This includes detailed information about the dotnet command results, build context, targets ran, projects built, lists of errors, etc.

If you have a specific process you'd like to support, you can define a concrete implementation derived from ITestEnvironmentRunner.

Getting Started

This getting started uses the ProcessRunner to execute a dotnet build process on a pre-defined test case project, within a side-effect free test environment. The purpose of this getting started is to demonstrate how to set up a test environment, execute a runner and assert the result.

ℹ️ This is only an example
We're using ProcessRunner here with the dotnet executable to illustrate getting started. The PlayNicely.Executor.DotNet package provides a specific implementation for dotnet that includes important context information after execution. If you are running dotnet tests, we recommend using that runner instead.

This getting started is code-first, in a typical configuration you would likely use the IDE to define test case projects and SpecFlow (or some other BDD framework) to define the environment.

Define the test case project

Let's create a scenario to test for a failing build, because the .NET target framework is invalid. First, define the test case project and file system.

var testCaseProject = new TestCaseProject("my-failing-project");
var projectFile = testCaseProject.Root.AddFile("proj.csproj");

testCaseProject.ProjectFile = projectFile;

using(var writer = new StreamWriter(projectFile.OpenWriteStream())
{
    writer.WriteLine("<Project Sdk=\"Microsoft.NET.Sdk\">");
    writer.WriteLine("  <PropertyGroup>");
    // Note invalid target framework...
    writer.WriteLine("    <TargetFramework>my-net9.0</TargetFramework>");
    writer.WriteLine("  </PropertyGroup>");
    writer.WriteLine("</Project>");
}

Define the environment

Use the TestEnvironmentBuilder to specify:

  • Command Management
    • RequiredCommands
      Any command that must be available in the test environment. Using this method ensures commands can always be executed, by finding them in the current $PATH, and creating a temporary bin directory with symbolic links to the actual executables. This bin directory is prepended to the $PATH in the subsequent environment. On Windows this is less likely to be required, but on Linux, most commands are in shared directories like /usr/bin or /usr/local/bin, if something is excluded (see next bullet point), by removing one of these paths, it is likely to exclude a lot of other commands, RequiredCommands ensures the essential commands can still be executed from the $PATH.
    • ExcludeCommandFromPath
      It is often neccesary, in a negative test case, to assume a program is not installed. This method locates a command on the current $PATH and removes any directories where it is found. The result of this, when running in the ITestEnvironment is that any attempt to run an excluded command will fail (because it isn't found on the $PATH).
  • Package Management
    • AddPackageSource
      Often, the usage of this package is to test another NuGet package project. So that the test environment can run release tests using the under development version of a package, this method allows overriding NuGet.Config in the test environment.

      ℹ️ If you specify a relative path for the source location, it is relative to your assembly's binary location.

    • ClearPackageSources
      Allows the list of package sources to be cleared so that machine wide sources can be excluded during testing.

    • MapPackagesToSource
      Map packages with a package name (pattern) to a specific source. So that you can test local packages rather than the most recent one published to nuget.org. This is important, if you are making a change to a package you want to test those changes locally.

    • PackageSourceFallback
      After mapping patterns to particular package sources, you need to set an all encompassing fallback package source. You can use this method to do that if you know which source, typically nuget.org. If you'd rather use a best default, see the next method.

    • UseBestPackageSourceFallback
      If you would rather just default the fallback package source, use this method. It will use the following logic to determine a fallback source.

      • use source named 'nuget.org'
      • use the first source that is https?:// and is using the domain 'nuget.org'
      • use the first source that is https:// based
      • use the first source that is http:// based
      • use any available source
  • SetProject
    The target project for this test environment, the project's file system will be materialized and set as the working directory for the process.
Example for clarity

Continuing context from here. We need to ensure dotnet is always available, and specify the test case target project.

var builder = new TestEnvironmentBuilder();
var testEnv = await builder.RequiredCommands("dotnet")
                           .SetProject(testCaseProject)
                           .BuildAsync();

Run the test

With the environment built, simply construct the runner and assert the result (which we expect to fail).

var runner = new ProcessRunner("dotnet", "build"); // Don't need to specify project path,
                                                   // process working dir is root of project
var testResult = await runner.ExecuteAsync(testEnv);

Assert.That(testResult.Succeeded, Is.False);

Why?

This project came about to support the use of Node.js packages within .NET projects in a .NET first way. You can achieve the integration of Node.js tools using plugins or other tooling. The problem with this (using plugins) is, these can often become out of date or stale. Most of the Node.js packages are developed by an active community, so accessing the latest npm packages directly makes the most sense (we get latest features and security updates).

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on PlayNicely.Executor:

Package Downloads
PlayNicely.Executor.DotNet

A framework that facilitates testing of Play Nicely functionality. Provides capability to execute dotnet commands, in a controlled environment, against test case projects.

PlayNicely.SpecFlow.Executor

SpecFlow bindings that allow you to run tests by executing programs against a pre-configured test environment.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.3.2-beta-550 92 10/4/2024
1.3.2-beta-543 95 9/28/2024
1.3.2-beta-535 101 9/28/2024
1.3.2-beta-529 93 9/23/2024
1.3.2-beta-518 107 9/21/2024
1.3.2-beta-511 90 9/20/2024
1.3.2-beta-509 98 9/20/2024
1.3.2-beta-507 87 9/20/2024
1.3.2-beta-505 94 9/19/2024
1.3.2-beta-501 104 9/18/2024
1.3.2-beta-499 96 9/18/2024
1.3.2-beta-496 96 9/18/2024
1.3.2-beta-494 101 9/18/2024
1.3.2-beta-492 113 9/18/2024
1.3.1 143 9/17/2024
1.3.1-beta-487 105 9/17/2024
1.3.0 141 9/14/2024
1.3.0-beta-479 109 9/14/2024
1.3.0-beta-472 113 9/14/2024
1.2.0 116 7/12/2024
1.2.0-beta-465 124 9/7/2024
1.2.0-beta-450 105 7/14/2024
1.2.0-beta-442 105 7/12/2024
1.1.1 142 6/1/2024
1.1.1-beta-432 102 7/11/2024
1.1.1-beta-418 115 6/1/2024
1.1.1-beta-398 113 6/1/2024
1.1.0 282 4/14/2024
1.1.0-beta-393 115 5/31/2024
1.1.0-beta-382 129 5/21/2024
1.1.0-beta-370 100 5/8/2024
1.1.0-beta-355 119 5/7/2024
1.1.0-beta-349 119 5/7/2024
1.1.0-beta-346 125 5/7/2024
1.1.0-beta-340 130 5/7/2024
1.1.0-beta-323 123 5/6/2024
1.1.0-beta-312 125 4/26/2024
1.1.0-beta-299 120 4/14/2024
1.1.0-beta-296 130 4/14/2024
1.0.4 174 4/11/2024
1.0.4-beta-287 122 4/11/2024
1.0.4-beta-282 133 4/11/2024
1.0.4-beta-280 130 4/10/2024
1.0.4-beta-278 129 4/10/2024
1.0.4-beta-276 117 4/10/2024
1.0.4-beta-274 138 4/9/2024
1.0.4-beta-272 129 4/9/2024
1.0.3 172 3/21/2024
1.0.3-beta-266 129 3/21/2024
1.0.3-beta-260 125 3/21/2024
1.0.2 191 3/10/2024
1.0.2-prerelease-20240301-0... 119 3/1/2024
1.0.2-beta-227 125 3/10/2024
1.0.2-beta-221 139 3/9/2024
1.0.2-beta-214 129 3/9/2024
1.0.2-beta-208 135 3/1/2024
1.0.2-beta-206 125 3/1/2024
1.0.1 125 2/29/2024
1.0.1-prerelease-20240229-1... 92 2/29/2024
1.0.1-prerelease-20240228-0... 92 2/28/2024
1.0.1-prerelease-20240226-1... 50 2/26/2024
1.0.1-prerelease-20240225-0... 56 2/25/2024
1.0.1-prerelease-20240225-0... 54 2/25/2024
1.0.0 497 2/10/2024
1.0.0-prerelease-20240210-1... 253 2/10/2024
1.0.0-prerelease-20240210-1... 233 2/10/2024
1.0.0-prerelease-20240209-1... 219 2/9/2024
1.0.0-prerelease-20240209-1... 239 2/9/2024
1.0.0-prerelease-20240209-1... 236 2/9/2024