{"id":37057064,"url":"https://github.com/fixie/fixie.assertions","last_synced_at":"2026-01-14T06:27:23.066Z","repository":{"id":256268950,"uuid":"849634286","full_name":"fixie/fixie.assertions","owner":"fixie","description":"Ergonomic Assertions for .NET","archived":false,"fork":false,"pushed_at":"2025-07-02T14:47:07.000Z","size":325,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-22T08:51:40.892Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://fixie.github.io","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/fixie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/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,"zenodo":null}},"created_at":"2024-08-30T00:35:27.000Z","updated_at":"2025-07-02T14:47:11.000Z","dependencies_parsed_at":"2024-11-13T20:36:27.765Z","dependency_job_id":null,"html_url":"https://github.com/fixie/fixie.assertions","commit_stats":null,"previous_names":["fixie/fixie.assertions"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/fixie/fixie.assertions","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fixie%2Ffixie.assertions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fixie%2Ffixie.assertions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fixie%2Ffixie.assertions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fixie%2Ffixie.assertions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fixie","download_url":"https://codeload.github.com/fixie/fixie.assertions/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fixie%2Ffixie.assertions/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28412211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T05:26:33.345Z","status":"ssl_error","status_checked_at":"2026-01-14T05:21:57.251Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":"2026-01-14T06:27:22.388Z","updated_at":"2026-01-14T06:27:23.058Z","avatar_url":"https://github.com/fixie.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fixie.Assertions\n\n## Ergonomic Assertions for .NET\n\nFixie.Assertions is a small assertion library. It is easy to understand and trivially open to extension. It produces highly readable error messages.\n\nAssertion libraries are orthogonal to test frameworks. You don't have to use Fixie if you use Fixie.Assertions, and you don't have to use Fixie.Assertions if you use Fixie. Still, they make for a great combination as they both stand on the same design premise:\n\n\u003e Developer ergonomics result from having a small and simple core that can be trivially extended by the user with idiomatic code.\n\nLike other .NET assertion libraries, error messages include context from the original assertion line, including the target expression found on the left of the assertion `.` operator. Unlike other libraries, though, this does not require injecting MSBuild properties into your test project, or stack trace walking, or a Release vs Debug build configuration preference, or tricky parsing of arbitrarily complex C# code. Your test failure messages are never tossed into a blender when things go wrong. Instead, the magic here is provided by the compiler with its inherent awareness of C# syntax using features introduced in .NET 8.\n\n## Equality with `ShouldBe`\n\n```cs\nage.ShouldBe(65);\n```\n\n```\nage should be 65 but was 30\n```\n\n```cs\nmarkdownDocument.ToString().ShouldBe(\n    \"\"\"\n    # Heading\n    \n    ## Subheading\n    \n    Paragraph including *emphasis*.\n    \"\"\"\n    );\n```\n\n```\nmarkdownDocument.ToString() should be\n\n    \"\"\"\n    # Heading\n    \n    ## Subheading\n    \n    Paragraph including *emphasis*.\n    \"\"\"\n\nbut was\n\n    \"\"\"\n    # Heading\n    \n    ## Subheading\n    \n    Paragraph including **typo**.\n    \"\"\"\n```\n\n\n## Structural Equality with `ShouldMatch`\n\nOther assertion libraries tend to treat array equality checks with structural equality semantics. Doing so complicates the understanding of equality-asserting method names, reduces the ability to say clearly what you mean, and overcomplicates matters in those situations where you meaningfully care whether *this* array is literally *that* array.\n\nWith Fixie.Assertions, `ShouldBe` uses idiomatic .NET equality semantics, full stop. When you instead want structural equality, you say so clearly with `ShouldMatch`.\n\n```cs\ndecimal[] prices = [1.20m, 5.99m, 10.14m];\nprices.ShouldBe(prices); // Succeeds.\nprices.ShouldBe([1.20m, 5.99m, 10.14m]); // Fails! .NET arrays do not overload `==`.\n```\n\n```\nprices should be\n\n    [\n        1.20,\n        5.99,\n        10.14\n    ]\n\nbut was\n\n    [\n        1.20,\n        5.99,\n        10.14\n    ]\n\nThese serialized values are identical. Did you mean to perform a structural comparison with `ShouldMatch` instead?\n```\n\n```cs\ndecimal[] prices = [1.20m, 5.99m, 10.14m];\nprices.ShouldMatch(prices); // Succeeds.\nprices.ShouldMatch([1.20m, 5.99m, 10.14m]); // Succeeds by structural comparison.\nprices.ShouldMatch([1.20m, 5.99m, 10.14m, 7.34m]); // Fails by structural comparison.\n```\n\n```\nprices should be\n\n    [\n        1.20,\n        5.99,\n        10.14,\n        7.34\n    ]\n\nbut was\n\n    [\n        1.20,\n        5.99,\n        10.14\n    ]\n```\n\n`ShouldMatch` will perform a deep comparison of public object state, even against anonymous-typed expectations:\n\n```\nmyComplexObject.ShouldMatch(new {\n    Property = \"ABC\",\n    Field = 123,\n    List = [1, 2, 3]\n    Dictionary = new Dictionary\u003cstring, int\u003e {\n        [\"A\"] = 1,\n        [\"B\"] = 2\n    }\n    Nested = new {\n        Property = \"DEF\"\n    }\n});\n```\n\n\u003e WARNING: Beware making `ShouldMatch` comparisons between types that have equivalent public structure but meaningfully-different state. You may fool yourself into thinking two objects are equivalent when you would in fact disagree. It is best to witness the textual representation of your type as seen when the assertion fails, as part of a typical \"Red, Then Green\" implementation of your test, when deciding whether structural comparison is appropriate for the types in question. As a reasonable heuristic, if you would feel unsafe serializing the two objects to JSON and asserting the resulting strings are equal, you should feel unsafe calling `ShouldMatch` for the same reason. As with JSON serialization, extremely nested objects or those with cycles are unsupported and will fail with an explanation.\n\n\n## Type Pattern Assertions\n\n```cs\nobject o = \"ABC\";\n\no.ShouldBe\u003cstring\u003e(); // Succeeds.\no.ShouldBe\u003cint\u003e(); // Fails.\n```\n\n```\no should match the type pattern\n\n    is int\n\nbut was\n\n    string\n```\n\n## Nulls\n\nThe `ShouldNotBeNull` assertion provides additional evidence to the compiler as it traces the flow of nullability through your test code. After calling it, the target object is understood to definitely not be null in subsequent statements.\n\n```cs\npossiblyNull.Property.ShouldBe(7);\n//          ^  Nullability warning here!\n```\n\n```cs\npossiblyNull.ShouldNotBeNull();\npossiblyNull.Property.ShouldBe(7); // No warning here.\n```\n\nUpon success, it returns the value unchanged with awareness that it is not null:\n\n```cs\npossiblyNull.ShouldNotBeNull().Property.ShouldBe(7); // No warning here.\n```\n\n## Expecting Exceptions\n\n```cs\nAction divideByZero = () =\u003e OperationThatDividesByZero();\n\ndivideByZero.ShouldThrow\u003cDivideByZeroException\u003e(); //Allow any message.\ndivideByZero.ShouldThrow\u003cDivideByZeroException\u003e(\"Divided By Zero\");\n```\n\nIf your operation fails to throw at all:\n\n```\ndivideByZero should have thrown System.DivideByZeroException but did not.\n```\n\nIf your operation throws the right exception type, but with the wrong message:\n\n```\ndivideByZero should have thrown System.DivideByZeroException with message\n\n    \"Divided By Zero\"\n\nbut instead the message was\n\n    \"Attempted to divide by zero.\"\n```\n\nIf your operation throws the wrong exception type, and you do not specify a message:\n\n```\ndivideByZero should have thrown System.DivideByZeroException\n\nbut instead it threw System.ArgumentNullException with message\n\n    \"Value cannot be null. (Parameter \\'divisor\\')\"\n```\n\nIf your operation throws the wrong exception type, and you do specify a message:\n\n```\ndivideByZero should have thrown System.DivideByZeroException with message\n\n    \"Attempted to divide by zero.\"\n\nbut instead it threw System.ArgumentNullException with message\n\n    \"Value cannot be null. (Parameter \\'divisor\\')\"\n```\n\n`ShouldThrow` is overloaded for `async`/`await` scenarios, where the operation under test is itself `async`:\n\n```cs\nFunc\u003cTask\u003e divideByZero = async () =\u003e await OperationThatDividesByZero();\n\nawait divideByZero.ShouldThrow\u003cDivideByZeroException\u003e(\"Divided By Zero\");\n\n```\n\n## `ShouldSatisfy`\n\nMost assertion libraries tend to accrete 1000 `ShouldXyz` methods for every conceivable situation. This library refuses to boil the ocean. The `ShouldSatisfy(expectation)` assertion reduces the need for things like `ShouldBeGreaterThan`, `ShouldBeGreaterThanOrEqualTo`, and similar nonidiomatic assertions.\n\n```cs\nvar value = 4;\nvalue.ShouldSatisfy(x =\u003e x \u003e 4);\n```\n\n```\nvalue should satisfy\n\n    \u003e 4\n\nbut was\n\n    4\n```\n\n```cs\nvar value = 3;\nvalue.ShouldSatisfy(x =\u003e x \u003e= 4);\n```\n\n```\nvalue should satisfy\n\n    \u003e= 4\n\nbut was\n\n    3\n```\n\n## Integration with Fixie\n\nThe properties on `ComparisonException` are a natural fit for display in your diff tool. When a single test fails an assertion comparing two objects, the following custom Fixie report will display the Expected/Actual values in your diff tool.\n\n```xml\n\u003cItemGroup\u003e\n  \u003cPackageReference Include=\"Fixie.TestAdapter\" /\u003e\n  \u003cPackageReference Include=\"Fixie.Assertions\" /\u003e\n  \u003cPackageReference Include=\"DiffEngine\" /\u003e\n\u003c/ItemGroup\u003e\n```\n\n```cs\nusing Fixie;\n\nnamespace Example.Tests;\n\nclass TestProject : ITestProject\n{\n    public void Configure(TestConfiguration configuration, TestEnvironment environment)\n    {\n        if (environment.IsDevelopment())\n            configuration.Reports.Add\u003cDiffToolReport\u003e();\n    }\n}\n```\n\n```cs\nusing Fixie.Reports;\nusing Fixie.Assertions;\nusing DiffEngine;\n\nnamespace Example.Tests;\n\nclass DiffToolReport : IHandler\u003cTestFailed\u003e, IHandler\u003cExecutionCompleted\u003e\n{\n    int failures;\n    Exception? singleFailure;\n\n    public Task Handle(TestFailed message)\n    {\n        failures++;\n\n        singleFailure = failures == 1 ? message.Reason : null;\n\n        return Task.CompletedTask;\n    }\n\n    public async Task Handle(ExecutionCompleted message)\n    {\n        if (singleFailure is ComparisonException exception)\n            await LaunchDiffTool(exception);\n    }\n\n    static async Task LaunchDiffTool(ComparisonException exception)\n    {\n        var tempPath = Path.GetTempPath();\n        var expectedPath = Path.Combine(tempPath, \"expected.txt\");\n        var actualPath = Path.Combine(tempPath, \"actual.txt\");\n\n        File.WriteAllText(expectedPath, exception.Expected);\n        File.WriteAllText(actualPath, exception.Actual);\n\n        await DiffRunner.LaunchAsync(expectedPath, actualPath);\n    }\n}\n```\n\nYour diff tool launches on failure, drawing attention to the meaningful differences between the expected and actual values:\n\n```diff\n[\n    1.20,\n    5.99,\n+    10.14\n-    10.14,\n-    7.34\n]\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffixie%2Ffixie.assertions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffixie%2Ffixie.assertions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffixie%2Ffixie.assertions/lists"}