From 397b0d388ef61c34dbd19aa373556a041f10ed54 Mon Sep 17 00:00:00 2001 From: Jan Krivanek Date: Wed, 26 Oct 2022 18:48:04 +0200 Subject: [PATCH] Add template verifier documentation --- docs/Templates-Testing-Tooling.md | 288 +++++++++++++++++- .../TemplateVerifierOptions.cs | 11 +- 2 files changed, 293 insertions(+), 6 deletions(-) diff --git a/docs/Templates-Testing-Tooling.md b/docs/Templates-Testing-Tooling.md index c1f6417b24..b8d7b00df6 100644 --- a/docs/Templates-Testing-Tooling.md +++ b/docs/Templates-Testing-Tooling.md @@ -1 +1,287 @@ -## TBD +# Overview + +Templates Testing Tooling is an engine for snapshot testing of templates instantiations - simplifying automated detection of regresions. It is surfaced via [API](#api) and [CLI tool](#cli). + +Tooling currently uses [Verify](https://github.com/VerifyTests/Verify) as an underlying engine to manage the snapshots generation and verification. The API surface of Verify is however intentionally hidden and not accessible. + +Engine creates a serialized version of outputs of tested scenario and compares those with stored snapshot (if any) and reports success or any found discrepancies. Snapshot created during the first run can be used as a baseline for future runs of the test scenario. + +## Naming of snapshots + +All snapshots are stored in a single folder that is by default named `Snapshots` and is located in a folder with test code (in case of API usage) or current folder (in case of CLI) usage. Location of the snapshots directory can be optionaly specified in API via [SnapshotsDirectory](#SnapshotsDirectory) parameter or in CLI via [-d|--snapshots-directory](#--snapshots-directory) option. + +All the files consisting a single snapshot of single scenario are stored in a single folder that is named with following convention: + +``` +..... +``` + + - `` - not used in CLI. Added by default in API. Can be opted out via [`DoNotPrependCallerMethodNameToScenarioName`](#DoNotPrependCallerMethodNameToScenarioName) + + - `` - used by default. Can be opted out in API via [`DoNotPrependTemplateNameToScenarioName`](#DoNotPrependTemplateNameToScenarioName) + + - `` - not used by default. Can be opted in in API via specifying [`ScenarioName`](#ScenarioName) + + - `` - used by default. Can be opted out in API via [`DoNotAppendTemplateArgsToScenarioName`](#DoNotAppendTemplateArgsToScenarioName) + + - `` - not used by default. Can be opted in in CLI via [`--unique-for`](#--unique-for) and in API via [`UniqueFor`](#UniqueFor) + + - `` - suffix, depending on whether the snapshot is a basline for test scenario (then the suffix is '.verified') or created by engine as unexpected/regressing the check (then the suffix is '.received'). + + +## Layout of snapshot files + +All files within snapshot has identical filenames and relative folder structure as files in the generated template output. + +Snapshot folder has the following layout: + +``` + + |-- + | |-- + | |-- + | |-- ... // layout is respecting folder structure of generated template + | + |-- // present if opted in into verifying command output + |--stderr.txt + |--stdout.txt +``` + +# API + +## Setup + +1. Add the package reference to `Microsoft.TemplateEngine.Authoring.TemplateVerifier`: + ```dotnetcli + dotnet add package Microsoft.TemplateEngine.Authoring.TemplateVerifier + ``` +2. Start using the `TemplateVerifier` in your tests: + ```cs +using Microsoft.TemplateEngine.Authoring.TemplateVerifier; + +namespace MyTests + +[TestClass] +public class MyTestClass +{ + [TestMethod] + public async Task MyTemplate_InstantiationTest() + { + TemplateVerifierOptions options = new(templateName: templateShortName) + { + TemplateSpecificArgs = new[] { "--arg1", "true" }, + }; + + VerificationEngine engine = new(_logger); + await engine.Execute(options).ConfigureAwait(false); + } +} + ``` + +## VerificationEngine + +Type to be used to execute the test scenarios. Can be constructed by passing `Microsoft.Extensions.Logging.ILogger` or `Microsoft.Extensions.Logging.ILoggerFactory` (the latter one leads to usage of separate log instances for the `dotnet new` process execution and for the actual verification engine). + +### Test scenario execution + +```cs +async Task Execute(IOptions optionsAccessor) +``` + +Successful run is indicated by `Task` that doesn't throw after awaiting. Failed scenario or snapshot expectation is indicated by `TemplateVerificationException` on the returned `Task`. + +### Configuration + +Configuration of the scenario run is done via `TemplateVerifierOptions` type. + +- **`string TemplateName`** - The name of the template to be verified. Can be already installed template or a template within local path specified with `TemplatePath` + +- **`string? TemplatePath`** - Applicable to local (not installed) template. The path to template.json file or containing directory. + +- **`IEnumerable? TemplateSpecificArgs`** - Instantiation arguments and options for the template. + +- **`string? SnapshotsDirectory`** - Custom location of directory with snapshots. Defaults to `Snapshots`. + +- **`bool VerifyCommandOutput`** - If set to true - 'dotnet new' command standard output and error contents will be verified along with the produced template files. + +- **`bool IsCommandExpectedToFail`** - If set to true - 'dotnet new' command is expected to return nonzero return code. Otherwise a zero exit code and no error output is expected. + +- **`bool DisableDiffTool`** - If set to true - the diff tool won't be automatically started by the `TemplateVerifier` on verification failures. + +- **`bool DisableDefaultVerificationExcludePatterns`** - If set to true - all template output files will be verified, unless `VerificationExcludePatterns` are specified. Otherwise a default exclusions (binaries and object files). + +- **`IEnumerable? VerificationExcludePatterns`** - Set of patterns defining files to be excluded from verification. [Globs](https://en.wikipedia.org/wiki/Glob_(programming)) patterns are recognized and honored. + +- **`IEnumerable? VerificationIncludePatterns`** - Set of patterns defining files to be included into verification (unless excluded by `VerificationExcludePatterns`). By default all files are included (unless excluded). [Globs](https://en.wikipedia.org/wiki/Glob_(programming)) patterns are recognized and honored. + +- **`string? OutputDirectory`** - Target directory to output the generated template. If explicitly specified, it won't be cleaned up upon successful run of test scenario. + +- **`string? SettingsDirectory`** - Settings directory for template engine (in memory location used if not specified). + +- **`UniqueForOption? UniqueFor`** - Indicating which scenarios should be differentiated. Differentiation performed by adjusting the snapshots directory name (see ) + + Currently supported values: + - Architecture + - OsPlatform + - Runtime + - RuntimeAndVersion + - TargetFramework + - TargetFrameworkAndVersion + +- **`string? DotnetNewCommandAssemblyPath`** - Applicable for internal tests of dotnet assembly. Path to custom assembly implementing the new command. + +- **`string? ScenarioName`** - Custom scenario name; if specified it will be used as part of verification snapshot name. + +- **`bool DoNotAppendTemplateArgsToScenarioName`** - `true`, if the instantiation args should not be appended to verification snapshot name. + +- **`bool DoNotPrependTemplateNameToScenarioName`** - `true`, if the template name should not be prepended to verification snapshot name. + +- **`bool DoNotPrependCallerMethodNameToScenarioName`** - `true`, if the caller method name should not be prepended to verification snapshot name. + +- **`string? StandardOutputFileExtension`** - Extension of autogeneratedfiles with stdout and stderr content. Defaults to `.txt`. + +- **`ScrubbersDefinition? CustomScrubbers`** - Delegates that perform custom scrubbing of template output contents before verifications. + + Scrubbers can as well be added via fluent API `WithCustomScrubbers`. Sample usage: + ```cs + options.WithCustomScrubbers( + ScrubbersDefinition.Empty + .AddScrubber(sb => sb.Replace("bb", "xx"), "cs") + .AddScrubber(sb => sb.Replace("cc", "yy"), "csproj") + // scrubber applicable to all files + .AddScrubber(sb => sb.Replace("aa", "zz")) + // supports multiple scrubbers per extension + .AddScrubber(sb => sb.Replace("cc", "**"), "cs") + // callback scrubber based on content filename or/and location + .AddScrubber((path, content) => + { + if (Path.GetExtension(path).Equals(".txt", StringComparison.OrdinalIgnoreCase)) + { + content.Replace("MyTemplate", "%TEMPLATE_NAME%"); + } + })); + ``` + +- **`VerifyDirectory? CustomDirectoryVerifier`** - Delegate that performs custom verification of template output contents. + + Custom verifier can as well be defined via fluent API `WithCustomDirectoryVerifier`. Sample usage: + + ```cs + options.WithCustomDirectoryVerifier( + async (contentDirectory, contentFetcher) => + { + await foreach (var (filePath, scrubbedContent) in contentFetcher.Value) + { + // Asserting on scrubbed file content + } + }); + ``` + + + +# CLI + +## Setup + +1. If you haven't setup your tools manifest file yet - set it up by running: + ```dotnetcli + dotnet new tool-manifest + ``` +2. Install the templeting authoring toolset: + ```dotnetcli + dotnet tool install Microsoft.TemplateEngine.Authoring.CLI + ``` + +## Synopsis + +```dotnetcli +dotnet dotnet-template-authoring verify [--template-args ] [-p|--template-path ] [-o|--output ] [-d|--snapshots-directory ] [--scenario-name ] [--disable-diff-tool] [--disable-default-exclude-patterns] [--exclude-pattern ] [--include-pattern ] [--verify-std] [--fail-expected] [--unique-for ] + [-h|--help] +``` + +## Description + +The `dotnet dotnet-template-authoring verify` command instantiates the specified while passing specified arguments, collects the resulting files (while applying filtering based on default and explicit exclude and include patterns) compares them to stored snapshots and reports the result. + +If there are differences or snapashot files are missing altogether (first run), the set of files is created within a folder with a `.received` suffix, that can be used to prepare proper snapshot files (those should be placed in folder with `.verified` suffix). + + + +## Arguments +- **``** + + Name of the template to be verified. Can be already installed template or a template within local path specified with `-p|--template-path option`. + +## Options + +- **`--template-args `** + + Template specific arguments - all joined into single enquoted string. Any needed quotations of actual arguments has to be escaped. + + +- **`-p|--template-path `** + + Specifies the path to the directory with template to be verified. + +- **`-o|-output`** + + Specifies the path to target directory to output the generated template to. + +- **`-d|--snapshots-directory `** + + Specifies path to the directory with snapshot files. + +- **`--scenario-name `** + + Specifies optional scenario name to be used in the snapshot folder name. + +- **`--disable-diff-tool`** + + If set to true - the diff tool won't be automatically started by the Verifier on verification failures. + +- **`--disable-default-exclude-patterns`** + + If set to true - all template output files will be verified, unless --exclude-pattern option is used. + +- **`--exclude-pattern `** + + Specifies pattern(s) defining files to be excluded from verification. + +- **`--include-pattern `** + + Specifies pattern(s) defining files to be included to verification (all files are included if not specified). + +- **`--verify-std`** + + If set to true - 'dotnet new' command standard output and error contents will be verified along with the produced template files. + +- **`--fail-expected`** + + If set to true - 'dotnet new' command is expected to return non-zero return code. + +- **`--unique-for `** + + Sets the Verifier expectations directory naming convention, by indicating which scenarios should be differentiated. + + + + ## Examples + +- Get the help for `verify` command + + ```dotnetcli + dotnet dotnet-template-authoring verify --help + ``` + +- Run the test of instantiation scenario for already installed template: + + ```dotnetcli + dotnet dotnet-template-authoring verify my-template-name --template-args "--arg1 value1 --arg2 \"multi word value\"" + ``` + +- Run the test of instantiation scenario for an installed template with verification of standard output and standard error streams of `dotnet new` process, while disabling automatic opening of a diff tool for snapshots: + + ```dotnetcli + dotnet dotnet-template-authoring verify console --verify-std --disable-diff-tool + ``` + diff --git a/src/Microsoft.TemplateEngine.Authoring.TemplateVerifier/TemplateVerifierOptions.cs b/src/Microsoft.TemplateEngine.Authoring.TemplateVerifier/TemplateVerifierOptions.cs index 59a6ca6772..ae7c1a54d8 100644 --- a/src/Microsoft.TemplateEngine.Authoring.TemplateVerifier/TemplateVerifierOptions.cs +++ b/src/Microsoft.TemplateEngine.Authoring.TemplateVerifier/TemplateVerifierOptions.cs @@ -17,7 +17,7 @@ public class TemplateVerifierOptions : IOptions public TemplateVerifierOptions(string templateName) => TemplateName = templateName; /// - /// Gets the name of locally installed template. + /// Gets the name of the template to be verified. Can be already installed template or a template within local path specified with . /// public string TemplateName { get; init; } @@ -48,7 +48,7 @@ public class TemplateVerifierOptions : IOptions /// /// If set to true - 'dotnet new' command is expected to return nonzero return code. - /// Otherwise a zero error code and no error output is expected. + /// Otherwise a zero exit code and no error output is expected. /// public bool IsCommandExpectedToFail { get; init; } @@ -76,6 +76,7 @@ public class TemplateVerifierOptions : IOptions /// /// Gets the target directory to output the generated template. + /// If explicitly specified, it won't be cleaned up upon successful run of test scenario. /// public string? OutputDirectory { get; init; } @@ -105,17 +106,17 @@ public class TemplateVerifierOptions : IOptions public string? ScenarioName { get; init; } /// - /// , if the instantiation args should not be appended to verification subdirectories. + /// , if the instantiation args should not be appended to verification snapshot name. /// public bool DoNotAppendTemplateArgsToScenarioName { get; init; } /// - /// , if the template name should not be prepended to verification subdirectories. + /// , if the template name should not be prepended to verification snapshot name. /// public bool DoNotPrependTemplateNameToScenarioName { get; init; } /// - /// , if the caller method name should not be prepended to verification subdirectories. + /// , if the caller method name should not be prepended to verification snapshot name. /// public bool DoNotPrependCallerMethodNameToScenarioName { get; init; }