{"id":21028213,"url":"https://github.com/exyi/checktestoutput","last_synced_at":"2025-07-22T03:34:56.880Z","repository":{"id":65544094,"uuid":"167195843","full_name":"exyi/CheckTestOutput","owner":"exyi","description":"Semi-manual asserts for .NET unit tests","archived":false,"fork":false,"pushed_at":"2024-07-10T18:35:54.000Z","size":179,"stargazers_count":1,"open_issues_count":1,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-06-21T14:05:20.954Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/exyi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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":"2019-01-23T14:22:44.000Z","updated_at":"2024-07-04T09:54:51.000Z","dependencies_parsed_at":"2024-11-19T12:00:49.993Z","dependency_job_id":"8a8c57fa-0a51-45a4-a5a1-9886b869d6b5","html_url":"https://github.com/exyi/CheckTestOutput","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/exyi/CheckTestOutput","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exyi%2FCheckTestOutput","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exyi%2FCheckTestOutput/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exyi%2FCheckTestOutput/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exyi%2FCheckTestOutput/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/exyi","download_url":"https://codeload.github.com/exyi/CheckTestOutput/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exyi%2FCheckTestOutput/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266420434,"owners_count":23925923,"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","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-11-19T11:54:21.732Z","updated_at":"2025-07-22T03:34:56.857Z","avatar_url":"https://github.com/exyi.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CheckTestOutput\n\n**A library for semi-manual test output verification. Asks you to `git add` the new output if it changed.**\n\nCheckTestOutput simply compares the test output with the last \"accepted\" file. When it differs, you get an error.\nIt is **compared with its version from git index** and throws an **exception if it does not match**, prints a diff and writes a new version to the working tree.\nTo accept the new version, you simply stage the changed file (`git add ...`).\nTo inspect the differences, you use your favorite diff tool.\n\nAlthough it's a nice idea that tests should verify if the results are correct by some smart logic, it not always possible/practical. For example, when testing a transpiler, it would come in handy to solve the halting problem. In such cases, you will end up with an `Assert.Equal(\"some very long code including many \\\" \\\" and \\n \\n, super fun to read and maintain\", generatedCode)`. This project just makes the long asserts less annoying.\n\n## Usage\n\nIt requires your project to be in [git version control system](https://git-scm.com/) (it works without git, but does not offer the simple stage-to-accept workflow).\nYou can use any test framework you want, this thing just throws exceptions -- we'll use XUnit in the examples here.\nThe `OutputChecker` constructor parameter specifies where are the output files (relative to the test file location - it uses [C#/F# caller info attributes](https://docs.microsoft.com/cs-cz/dotnet/csharp/programming-guide/concepts/caller-information)).\nIn this case, it's `./testoutputs`.\nThe file name will be equal to the caller method name, in this case, `SomeTest.TestString.txt`.\n\nThis is how you check if simple string matches:\n\n```csharp\npublic class SomeTest\n{\n    OutputChecker check = new OutputChecker(\"testoutputs\");\n    [Fact]\n    public void TestString()\n    {\n        string someData = DoSomeComputation();\n        check.CheckString(someData);\n    }\n}\n```\n\nYou can also check if a collection of lines matches:\n\n```csharp\n[Fact]\npublic void TestLines()\n{\n    IEnumerable\u003cstring\u003e someData = GetSomeResults().Select(a =\u003e a.ToString());\n    check.CheckLines(someData);\n}\n```\n\nCheck if the object matches when it is serialized to JSON (using Newtonsoft.Json)\n\n```csharp\n[Fact]\npublic void TestObject()\n{\n    PersonViewModel someData = GetDefaultPersonDetail();\n    check.CheckJsonObject(someData);\n}\n```\n\nTo use more that one check in one test, you need to give them names (so they don't end up overriding themselves):\n\n```csharp\n[Fact]\npublic void TestWithMultipleChecks()\n{\n    PersonViewModel person = GetDefaultPersonDetail();\n    check.CheckString(someData.CurriculumVitae, checkName: \"cv\");\n    check.CheckString(someData.Genome, checkName: \"genome\");\n}\n```\n\nAlternatively, combine them into one anonymous JSON object. It's generally preferable when the string are short - too many tiny files are annoying:\n\n```csharp\n[Fact]\npublic void TestObject()\n{\n    PersonViewModel person = GetDefaultPersonDetail();\n    check.CheckJsonObject(new {\n        fname = person.Name,\n        lname = person.LastName,\n        person.BirthDate\n    });\n}\n```\n\nThe `checkName` parameter is also useful for tests with parameters (`[Theory]` in XUnit).\nFor example, this way we could test a regular expression:\n\n```csharp\n[Theory]\n[InlineData(\"positive\", \"it's 12th January\")]\n[InlineData(\"negative\", \"the result is -1\")]\n[InlineData(\"zero\", \"there is 0% growth\")]\npublic void IncrementNumbers(string checkName, string testString)\n{\n    var replacedString = Regex.Replace(testString, \"-?\\\\d+\", m =\u003e int.Parse(m.Value) + 1 + \"\");\n    check.CheckLines(new [] {\n        testString,\n        \" -\u003e \",\n        replacedString\n    }, checkName);\n}\n```\n\nThe text files have `.txt` file extension by default, but it's easy to change:\n\n```csharp\n[Fact]\npublic void GenerateSomeCsharpCode()\n{\n    string code = GimmeSourceCode();\n    check.CheckString(code, fileExtension: \"cs\");\n}\n```\n\nJust keep in mind that dotnet is going to treat these `.cs` files as part of source code unless you `\u003cCompile Remove=\"testoutputs/**.cs\" /\u003e` them in the `.csproj` file.\n\nBinary data can be checked using `check.CheckBinary(byte[])` method. No diff is printed in that case, you have to use an external tool to diff binary files.\n\n### F#\n\nCheckTestOutput is reasonably F# friendly, although it's written in C#:\n\n```fsharp\nopen CheckTestOutput\n\nlet check = OutputChecker \"testoutputs\"\n\n[\u003cFact\u003e]\nlet ``Simple object processing - UseGenericUnion`` () =\n    computeSomething 123 \"456\"\n    |\u003e string\n    |\u003e check.CheckString\n\n    // or if you need checkName\n    check.CheckString (\"test string\", checkName = \"test2\")\n[\u003cFact\u003e]\nlet ``Example with anonymous record`` () =\n    check.CheckJsonObject {| a = 1; b = \"tukabel\" |}\n```\n\n### Non-deterministic strings\n\nIf the test output contains some randomly generated UUIDs it isn't possible to test that the output is always the same. To fix the problem, you would either have to use a seeded random generator, or replace the UUIDs after the fact. CheckTestOutput has a helper functionality which allows you to replace random UUIDs with deterministically generated ones.\n\nYou can enable it by setting `sanitizeGuids: true` when creating `OutputChecker` (or `sanitizeQuotedGuids` to sanitize GUIDs in quotes):\n\n```csharp\nOutputChecker check = new OutputChecker(\"testoutputs\", sanitizeGuids: true);\n\n[Fact]\npublic void CheckGuidsJson()\n{\n    var id1 = Guid.NewGuid();\n    var id2 = Guid.NewGuid();\n\n    check.CheckJsonObject(new { id1, id2, id3 = id1 });\n}\n```\n\nThe sanitization preserves equality - it replaces different UUIDs with different stub string and same UUID with the same string. In this case, the checked JSON will be this:\n\n```json\n{\n\t\"id1\": \"aaaaaaaa-bbbb-cccc-dddd-000000000001\",\n\t\"id2\": \"aaaaaaaa-bbbb-cccc-dddd-000000000002\",\n\t\"id3\": \"aaaaaaaa-bbbb-cccc-dddd-000000000001\"\n}\n```\n\nWhile mostly used for UUIDs, we can replace anything that can be found by a regular expression - just specify a list of regular expressions in the `nonDeterminismSanitizers` parameter.\n\n### Custom checks\n\nThe `CheckString`, `CheckLines` and `CheckJsonObject` are just extension methods on the `OutputChecker` class and you can write your own.\nThe only thing to keep in mind is to include and propagate the `CallerMemberName` and `CallerFilePath` attributes.\nAs a simple example, this is how to implement a simple helper that changes the default file extension to `js`:\n\n```csharp\npublic static void CheckJavascript(\n    this OutputChecker t,\n    string output,\n    string checkName = null,\n    string fileExtension = \"js\",\n    [System.Runtime.CompilerServices.CallerMemberName] string memberName = null,\n    [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = null)\n{\n    t.CheckString(output, checkName, fileExtension, memberName, sourceFilePath);\n}\n\n```\n\nFor more inspiration, have a look at [CheckExtensions class in the Coberec project](https://github.com/exyi/coberec/blob/83f4a744af8cc9ec2c3e24d86d25840c41617ed2/src/Coberec.ExprCS.Tests/CheckExtensions.cs).\n\n## Installation\n\n[NuGet package](https://www.nuget.org/packages/CheckTestOutput) ¯\\\\_(ツ)_/¯.\n\n```\ndotnet add package CheckTestOutput\n```\n\nAlternatively, you can just grab the source codes from `src` folder and copy them into your project (it's MIT licensed, so just keep a link to this project in the copied code).\nThis library does not have any other dependencies (on new enough dotnet).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexyi%2Fchecktestoutput","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexyi%2Fchecktestoutput","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexyi%2Fchecktestoutput/lists"}