{"id":19466817,"url":"https://github.com/dena/dena.codeanalysis.testing","last_synced_at":"2025-04-25T11:30:36.572Z","repository":{"id":47190501,"uuid":"339064895","full_name":"DeNA/Dena.CodeAnalysis.Testing","owner":"DeNA","description":"TDD friendly test helpers for Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer","archived":false,"fork":false,"pushed_at":"2025-04-15T03:31:23.000Z","size":120,"stargazers_count":10,"open_issues_count":1,"forks_count":2,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-15T04:32:57.757Z","etag":null,"topics":["roslyn","testing"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DeNA.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-15T12:05:20.000Z","updated_at":"2024-04-26T02:15:50.000Z","dependencies_parsed_at":"2024-02-05T18:55:06.047Z","dependency_job_id":"8dccf9b8-4db5-4b3f-b90d-ec778c28dd8c","html_url":"https://github.com/DeNA/Dena.CodeAnalysis.Testing","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DeNA%2FDena.CodeAnalysis.Testing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DeNA%2FDena.CodeAnalysis.Testing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DeNA%2FDena.CodeAnalysis.Testing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DeNA%2FDena.CodeAnalysis.Testing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DeNA","download_url":"https://codeload.github.com/DeNA/Dena.CodeAnalysis.Testing/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250808044,"owners_count":21490612,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["roslyn","testing"],"created_at":"2024-11-10T18:30:28.346Z","updated_at":"2025-04-25T11:30:36.292Z","avatar_url":"https://github.com/DeNA.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"Dena.CodeAnalysis.Testing\n=========================\n[![NuGet version](https://badge.fury.io/nu/Dena.CodeAnalysis.Testing.svg)](https://www.nuget.org/packages/Dena.CodeAnalysis.Testing/)\n[![CI](https://github.com/DeNA/Dena.CodeAnalysis.Testing/actions/workflows/ci.yml/badge.svg?branch=master\u0026event=push)](https://github.com/DeNA/Dena.CodeAnalysis.Testing/actions/workflows/ci.yml)\n\n\nThis library provides TDD friendly DiagnosticAnalyzer test helpers:\n\n* DiagnosticAnalyzerRunner\n\n    A runner for [`Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.diagnostics.diagnosticanalyzer?view=roslyn-dotnet).\n    The purpose of the runner is providing another runner instead of [`Microsoft.CodeAnalysis.Analyzer.Testing.AnalyzerVerifier.VerifyAnalyzerAsync`](https://github.com/dotnet/roslyn-sdk/blob/3046d1dffafd47ced55e4b76fd865179154c87ab/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerVerifier%603.cs#L13-L19).\n\n    Because of the `AnalyzerVerifier` has several problems:\n\n    1. Using AnalyzerVerifier, it is hard to instantiate analyzer with custom arguments (the custom args may be needed if your analyzer is composed by several smaller analyzer-like components)\n    2. AnalyzerVerifier may throw some exceptions because it test Diagnostics. But it should be optional because analyzer-like smaller components may not need it. If it is not optional the tests for the components become to need to wrap try-catch statements for each call of `VerifyAnalyzerAsync`\n\n* Test Doubles for DiagnosticAnalyzer\n    * NullAnalyzer: it do nothing\n    * StubAnalyzer: it analyze codes with a `Dena.CodeAnalysis.Testing.AnalyzerActions`\n    * SpyAnalyzer: it analyze codes and do not report any Diagnostics, but instead it records all actions that registered via [`Microsoft.CodeAnalysis.Dignostics.AnalysisContext`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.diagnostics.analysiscontext?view=roslyn-dotnet)\n\n\nRequirements\n------------\n\n* .NET Standard 2.1 or later\n\n\n\nUsage\n-----\n\n### Run DiagnosticAnalyzer\n\n```c#\nvar analyzer = new YourAnalyzer();\n\n// The analyzer get intialized and get to call registered actions.\nawait DiagnosticAnalyzerRunner.Run(\n    analyzer,\n    @\"public static class Foo\n{\n    public static void Bar()\n    {\n        System.Console.WriteLine(\"\"Hello, World!\"\");\n    }\n}\");\n```\n\n\n\n### Get Diagnostics\n\n```c#\nvar analyzer = new YourAnalyzer();\n\nvar diagnostics = await DiagnosticAnalyzerRunner.Run(\n    analyzer,\n    @\"public static class Foo\n{\n    public static void Bar()\n    {\n        System.Console.WriteLine(\"\"Hello, World!\"\");\n    }\n}\");\n\nAssert.AreEqual(0, diagnostics.Length);\n```\n\n\n\n### Assert Locations\n```c#\nvar location = diagnostic.Location;\n\nLocationAssert.HaveTheSpan(\n    \"/0/Test0.\",             // Optional. Skip path assertion if the path not specified,  \n    new LinePosition(1, 0),\n    new LinePosition(8, 5),\n    location\n);\n```\n\n\n\n### Print Diagnostics\n```c#\nvar diagnostics = await DiagnosticAnalyzerRunner.Run(\n    anyAnalyzer,\n    @\"\ninternal static class Foo\n{\n    internal static void Bar()\n    {\n        System.Console.WriteLine(\"\"Hello, World!\"\");\n    }\n}\nERROR\");\n\nAssert.AreEqual(0, diagnostics.Length, DiagnosticsFormatter.Format(diagnostics));\n// This message is like:\n//\n//   // /0/Test0.cs(9,1): error CS0116: A namespace cannot directly contain members such as fields or methods\n//   DiagnosticResult.CompilerError(\"\"CS0116\"\").WithSpan(\"\"/0/Test0.cs\"\", 9, 1, 9, 6),\n```\n\n\n\n### Check whether the DiagnosticAnalyzer.Initialize have been called\n\n```c#\nvar spyAnalyzer = new SpyAnalyzer();\n\nvar diagnostics = await DiagnosticAnalyzerRunner.Run(\n    spyAnalyzer,\n    @\"public static class Foo\n{\n    public static void Bar()\n    {\n        System.Console.WriteLine(\"\"Hello, World!\"\");\n    }\n}\");\n\nAssert.IsTrue(spyAnalyzer.IsInitialized);\n```\n\n\n\n### Check recorded actions\n\n```c#\nvar spyAnalyzer = new SpyAnalyzer();\n\nvar diagnostics = await DiagnosticAnalyzerRunner.Run(\n    spyAnalyzer,\n    @\"public static class Foo\n{\n    public static void Bar()\n    {\n        System.Console.WriteLine(\"\"Hello, World!\"\");\n    }\n}\");\n\n// CompilationActionHistory hold the Compilation object that given\n// to the action registered by AnalysisContext.RegisterCompilationAction.\nAssert.AreEqual(1, spyAnalyzer.CompilationActionHistory.Count);\n\n// Other available histories are:\n//\n//   - spyAnalyzer.CodeBlockActionHistory\n//   - spyAnalyzer.CodeBlockStartActionHistory\n//   - spyAnalyzer.CompilationActionHistory\n//   - spyAnalyzer.CompilationStartActionHistory\n//   - spyAnalyzer.OperationActionHistory\n//   - spyAnalyzer.OperationBlockActionHistory\n//   - spyAnalyzer.OperationBlockStartAction\n//   - spyAnalyzer.OperationBlockStartActionHistory\n//   - spyAnalyzer.SemanticModelActionHistory\n//   - spyAnalyzer.SymbolActionHistory\n//   - spyAnalyzer.SymbolStartActionHistory\n//   - spyAnalyzer.SyntaxNodeActionHistory\n//   - spyAnalyzer.SyntaxTreeActionHistory\n```\n\n\n\n### Do something in action\n\n```c#\nvar stubAnalyzer = new StubAnalyzer(\n    new AnalyzerActions\n    {\n        CodeBlockStartAction = context =\u003e DoSomething()\n    }\n);\n\nawait DiagnosticAnalyzerRunner.Run(\n    stubAnalyzer,\n    @\"public static class Foo\n{\n    public static void Bar()\n    {\n        System.Console.WriteLine(\"\"Hello, World!\"\");\n    }\n}\");\n\n// Other available actions are:\n//\n//   - stubAnalyzer.CodeBlockAction\n//   - stubAnalyzer.CodeBlockStartAction\n//   - stubAnalyzer.CompilationAction\n//   - stubAnalyzer.CompilationStartAction\n//   - stubAnalyzer.OperationAction\n//   - stubAnalyzer.OperationBlockAction\n//   - stubAnalyzer.OperationBlockStartAction\n//   - stubAnalyzer.OperationBlockStartAction\n//   - stubAnalyzer.SemanticModelAction\n//   - stubAnalyzer.SymbolAction\n//   - stubAnalyzer.SymbolStartAction\n//   - stubAnalyzer.SyntaxNodeAction\n//   - stubAnalyzer.SyntaxTreeAction\n```\n\n### DiagnosticAssert Class\n\n#### AreEqual\n\n`DiagnosticAssert.AreEqual` assert that collections of Diagnostics for equality.\n\nThrow an assert exception if given collections satisfy the following condition:\n\nElements that are only contained on one side. The equivalence is based on following properties\n\n- File path (e.g., path/to/file.cs)\n- Location of the `Diagnostic` (starting line number, starting character position)-(finishing line number, finishing\n  character position)\n- Identifier of the `DiagnosticDescriptor` (DDID) (e.g., CS0494)\n- `DiagnosticMessage` (e.g., The field 'C.hoge' is assigned but its value is never used)\n\nOtherwise, do nothing.\n\n```csharp\n[Test]\npublic async Task M()\n{\n    var analyzer = new YourAnalyzer();\n    const string testData = @\"\nclass C\n{\n    string {|hoge|CS0414|The field 'C.hoge' is assigned but its value is never used|} = \"\"Forgot semicolon string\"\"\n}\";\n\n    var (source, expected) = TestDataParser.CreateSourceAndExpectedDiagnosticFromFile(testData);\n    var actual = await DiagnosticAnalyzerRunner.Run(analyzer, source);\n    DiagnosticsAssert.AreEqual(expected, actual);\n}\n```\n\nOutput example\n\n```\nMissing 0 diagnostics, extra 1 diagnostics of all 2 diagnostics:\n    extra\t/0/Test0.cs: (3,43)-(3,43), CS1002, ; expected\n```\n\n#### IsEmpty\n\n`DiagnosticAssert.IsEmpty` assert that the `Diagnositc` is no exist.\n\nThrow an assert exception if given collections exist any `Diagnostic`.\n\nThe output format and equivalence is the same as `DiagnosticAssert.AreEqual`.\n\nOtherwise, do nothing.\n\n```csharp\n[Test]\npublic async Task M()\n{\n    var analyzer = new YourAnalyzer();\n    var source = @\"\nclass C\n{\n}\"; \n    var actual = await DiagnosticAnalyzerRunner.Run(analyzer, source);\n    DiagnosticsAssert.IsEmpty(actual);\n}\n```\n\n### TestDataParser Class\n\n#### CreateSourceAndExpectedDiagnostics\n\nCreate source and expected diagnostic from formatting embedded.\n\n```csharp\n[Test]\npublic async Task M()\n{\n    var analyzer = new YourAnalyzer();\n    const string testData = @\"\nclass C\n{\n    string {|hoge|CS0414|The field 'C.hoge' is assigned but its value is never used|} = \"\"Forgot semicolon string\"\"\n}\";\n\n    var (source, expected) = TestDataParser.CreateSourceAndExpectedDiagnosticFromFile(testData);\n}\n```\n\nThe `testData` variable has formatting embedded in the source.\n\nYou can parse this format using `CreateSourceAndExpectedDiagnosticFromFile` and get the source and expected Diagnostics.\n\n- source\n\n```csharp\nclass C\n{\n    string hoge = \"Forgot semicolon string\"\n}\n```\n\n- expected Diagnostics\n\n  - Location\n    - (3,11)-(3,15)\n  - DDID\n    - CS0414\n  - DiagnosticMessage\n    - The field 'C.hoge' is assigned but its value is never used\n\nSpecify the part to be reported in the following format.\n\nThe format is enclosed in `{ }` and separated by `|`.\n\n```\n{|source|DDID|DiagnosticMessage|}\n```\n\n\n\nLicense\n-------\n\n[MIT license](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdena%2Fdena.codeanalysis.testing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdena%2Fdena.codeanalysis.testing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdena%2Fdena.codeanalysis.testing/lists"}