{"id":15066163,"url":"https://github.com/liammorrow/oatmilk","last_synced_at":"2025-04-10T13:42:44.290Z","repository":{"id":249990057,"uuid":"833129067","full_name":"LiamMorrow/Oatmilk","owner":"LiamMorrow","description":"A declarative testing library for .NET, heavily inspired by Jest and Mocha","archived":false,"fork":false,"pushed_at":"2025-04-07T06:57:22.000Z","size":255,"stargazers_count":23,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-07T07:34:51.425Z","etag":null,"topics":["dotnet","dotnet-core","jest","testing","xunit"],"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/LiamMorrow.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":"2024-07-24T12:11:53.000Z","updated_at":"2025-04-07T06:57:15.000Z","dependencies_parsed_at":"2024-08-18T00:20:32.243Z","dependency_job_id":"fcf70484-086d-45db-b129-1c601dd3876d","html_url":"https://github.com/LiamMorrow/Oatmilk","commit_stats":{"total_commits":90,"total_committers":2,"mean_commits":45.0,"dds":0.0888888888888889,"last_synced_commit":"15b04c2ba287cf6c994da1de512b837cb8e94677"},"previous_names":["liammorrow/detest","liammorrow/detestable","liammorrow/oatmilk"],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiamMorrow%2FOatmilk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiamMorrow%2FOatmilk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiamMorrow%2FOatmilk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiamMorrow%2FOatmilk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LiamMorrow","download_url":"https://codeload.github.com/LiamMorrow/Oatmilk/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248226249,"owners_count":21068168,"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":["dotnet","dotnet-core","jest","testing","xunit"],"created_at":"2024-09-25T01:02:39.539Z","updated_at":"2025-04-10T13:42:44.262Z","avatar_url":"https://github.com/LiamMorrow.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\u003ca href=\"https://www.nuget.org/packages/Oatmilk/\"\u003e\u003cimg src=\"https://img.shields.io/nuget/v/Oatmilk?style=flat\u0026label=Oatmilk\" alt=\"NuGet Version\"\u003e\u003c/a\u003e\n\u003ca href=\"https://www.nuget.org/packages/Oatmilk.Nunit/\"\u003e\u003cimg alt=\"NuGet Version\" src=\"https://img.shields.io/nuget/v/Oatmilk.Nunit?style=flat\u0026label=Oatmilk.Nunit\"\u003e\u003c/a\u003e\n\u003ca href=\"https://www.nuget.org/packages/Oatmilk.Xunit/\"\u003e\u003cimg alt=\"NuGet Version\" src=\"https://img.shields.io/nuget/v/Oatmilk.Xunit?style=flat\u0026label=Oatmilk.Xunit\"\u003e\u003c/a\u003e\n\u003ca href=\"https://www.nuget.org/packages/Oatmilk.MSTest/\"\u003e\u003cimg alt=\"NuGet Version\" src=\"https://img.shields.io/nuget/v/Oatmilk.MSTest?style=flat\u0026label=Oatmilk.MSTest\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opensource.org/licenses/MIT\" rel=\"nofollow\"\u003e\u003cimg alt=\"License: MIT\" src=\"https://img.shields.io/badge/License-MIT-yellow.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://discord.gg/QbmMq6snYg\"\u003e\u003cimg src=\"https://img.shields.io/discord/1267395588513726605?logo=discord\" alt=\"Discord\"/\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\u003ca href=\"https://github.com/LiamMorrow/Oatmilk/actions\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/LiamMorrow/Oatmilk/build.yml\" alt=\"GitHub Actions Workflow Status\"\u003e\u003c/a\u003e\n\u003ca href=\"https://codecov.io/github/LiamMorrow/Oatmilk\"\u003e\u003cimg alt=\"codecov\" src=\"https://codecov.io/github/LiamMorrow/Oatmilk/graph/badge.svg?token=5UVDXIJVGV\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c!-- A spacer --\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\u003cimg alt=\"Oatmilk Mascot - Oatie\" src=\"./Assets/Oatie.png\" width=256/\u003e\u003c/p\u003e\n\n\u003ch2 align=\"center\"\u003e🥛 Refreshing .NET Testing\u003c/h2\u003e\n\n\u003e \"Jest is great, let's bring it to .NET\" - Me\n\nOatmilk is a testing library for .NET which allows you to write declarative tests, free from annotations and long method names. It is heavily inspired by the [jest](https://github.com/jestjs/jest) and [mocha](https://github.com/mochajs/mocha) testing frameworks in the JavaScript ecosystem.\n\nOatmilk currently supports running in a test project configured with [xunit](https://github.com/xunit/xunit) or [nunit](https://https://nunit.org/), with limited support for [MSTest](https://github.com/microsoft/testfx/tree/main). You can run your existing xunit/nunit `Facts` and `Tests` alongside `Oatmilk` tests, so you don't need to convert your entire test suite to Oatmilk all at once.\n\nNote that Oatmilk does not intend to be a full test framework, as such, things like mocking and asserting are out of scope. The assertions provided by Xunit/Nunit/MsTest are entirely compatible with Oatmilk. There are also many other great tools for the job. Check out [NSubstitute](https://nsubstitute.github.io/) for your mocking needs.\n\n## Getting Started\n\nFirst in your test project, install the appropriate Oatmilk package.\n\n#### Dotnet CLI\n\n```bash\ndotnet add package Oatmilk.Nunit\n# OR dotnet add package Oatmilk.Xunit\n# OR dotnet add package Oatmilk.MSTest\n```\n\nThen create a test class, and create your first `Oatmilk Test` by using the `Describe` attribute on a method. Be sure to include a static import of `Oatmilk.TestBuilder`. If using MSTest, your test class will still require a `[TestClass]` attribute.\n\n```csharp\nusing Oatmilk;\nusing static Oatmilk.TestBuilder;\n\n// [TestClass] if using MSTest\npublic class MyTestClass\n{\n    [Describe(\"My Test Suite\")]\n    public void Spec()\n    {\n        // Describe as many tests as you'd like\n        It(\"Should pass\", () =\u003e Assert.True(true));\n\n        It(\"Is another test, wow!\", () =\u003e Assert.Equal(1, 1));\n    }\n\n    [Fact]\n    public void ExistingXunitTest()\n    {\n        // You don't need to throw away your existing tests - they can live alongside Oatmilk tests :)\n    }\n}\n\n```\n\nYour tests should now show up in your IDE, and can be run with:\n\n```bash\ndotnet test\n```\n\nYou can run specific tests by filtering:\n\n```bash\ndotnet test --filter \"Name~My Test Suite\"\n```\n\n### Test setup and teardown\n\nIt is often useful to have setup and teardown methods that run around tests.\nOatmilk exposes these mechanisms through four methods:\n\n- `BeforeAll` - Runs ONCE before any test has run in its scope and child scopes\n- `BeforeEach` - Runs before EVERY test in its scope and child scopes\n- `AfterEach` - Runs after EVERY test in its scope and child scopes\n- `AfterAll` - Runs ONCE after all tests have run in its scope and child scopes\n\nEach of these mechanisms support async operations.\n\nScopes are defined by `Describe` blocks and can be nested.\n\nExample:\n\n```cs\npublic class MyTestClass\n{\n    [Describe(\"My Test Suite\")]\n    public void Spec()\n    {\n        Guid aGuidThatIsUniqueForEachTest;\n\n        BeforeAll(()=\u003e\n        {\n            // This will run once if any of the tests are run\n        })\n\n        BeforeEach(()=\u003e\n        {\n            // This will run every single time a test is run\n            // You can put initialization code here\n            aGuidThatIsUniqueForEachTest = Guid.NewGuid();\n        })\n\n        It(\"Is a test in the top scope\", () =\u003e\n        {\n            // At this stage, the BeforeAll, and BeforeEach defined above will have run for this test\n        })\n\n        AfterEach(ctx =\u003e{\n            // This will run after every single test in this scope and child scopes\n            // ctx contains information about the test which just ran (did it pass?)\n        })\n\n        Describe(\"A nested scope\", () =\u003e\n        {\n            BeforeEach(()=\u003e{\n                // This will only run for test which are declared in this scope, or any scopes declared WITHIN this scope\n            })\n\n            It(\"Is a nested test\", () =\u003e {\n                // At this stage:\n                // The first BeforeAll will have only run ONCE - even if we are running both tests\n                // The BeforeEach in the parent scope ran for this test\n                // The BeforeEach in this scope ran for this test\n            })\n        })\n\n    }\n}\n\n```\n\n## Features\n\n### Type Safe Test Enumeration\n\nProviding tests with dynamic data has next to zero ceremony. There's no need to annotate with a method with `[Theory]`, or supply data through a different class / member with `[MemberData]`, which has many pitfalls and moves the information of the test far from it. Simply use an `It.Each` or a `Describe.Each` method call and provide the data directly.\n\n```cs\n\nIt.Each(\n    [1, 3, 5],\n    val =\u003e $\"Asserts that {val} is odd\" // Format string are supported as well: \"{0} is odd\",\n    (value)=\u003e\n    {\n        (value % 2).Should().Be(1);\n    });\n```\n\n### Easily Skip Tests\n\nGot a test you'd like to skip for the time being? Simply add a `.Skip` to the test or describe block and it will be skipped.\n\n```cs\n\nIt.Skip(\"should be skipped\", () =\u003e Assert.True(false));\n\nDescribe.Skip(\"every test in this scope will be skipped\", () =\u003e\n{\n    It(\"will be skipped\", () =\u003e Assert.True(false));\n})\n\n```\n\n### Isolate Tests With Only\n\nFocusing on a single test in a spec? Simply use `It.Only` or `Describe.Only` and Oatmilk will skip every other test in that spec.\n\n```cs\n\nIt.Only(\"will run\", () =\u003e Assert.True(true));\n\nIt(\"will not run since another test uses It.Only\", () =\u003e Assert.True(false));\n\n```\n\n```cs\nDescribe.Only(\"A situation where all other tests will be skipped\", () =\u003e\n{\n    It(\"will run\", () =\u003e Assert.True(true));\n})\n\nIt(\"will not run because the describe block above uses .Only\", () =\u003e Assert.True(false));\n\n```\n\n### Fluent Syntax\n\nMany people use the [csharpier](https://github.com/belav/csharpier) formatter to format their code. Unfortunately, the way that it chops arguments produces somewhat less than nice to read Oatmilk test code, as it will put the description of the test on a new line:\n\n```cs\nIt(\n    \"is a test\",\n    () =\u003e\n    {\n        // My test code\n    }\n)\n\n```\n\nFor this reason, `Oatmilk` exposes a fluent API where the body of the test is supplied in a fluent manner, rather than as a second argument:\n\n```cs\nIt(\"is a test\")\n    .When(() =\u003e\n    {\n        // My test code\n    });\n```\n\nThis is purely stylistic. The two methods are functionally identical. If you don't like the name \"When\", simply create an extension method on the `ItBlock` and `DescribeBlock` classes with your chosen name. `Describe` has a similar approach, however it uses the method `As`, rather than `When`:\n\n```cs\nDescribe(\"My test suite\")\n    .As(() =\u003e\n    {\n        // My describe block\n    })\n```\n\n### IDE Feature Matrix\n\nEach test runner exposes different level of support for Oatmilk. All of them support running the entire suite of tests.\n\n| Feature                                            | Nunit | Xunit | MSTest |\n| -------------------------------------------------- | ----- | ----- | ------ |\n| Running all tests via `dotnet test`                | ✅    | ✅    | ✅     |\n| Running individual test via `dotnet test --filter` | ✅    | ✅    | ✅     |\n| Debugging entire test suite via IDE                | ✅    | ✅    | ✅     |\n| Debugging single test via IDE                      | ✅    | ✅    | ❌     |\n| Run individual tests via IDE                       | ✅    | ✅    | ❌     |\n| Jump to `It` block via IDE                         | ✅    | ✅    | ❌     |\n| Run/Debug specific `Describe` block via IDE        | ✅    | ❌    | ❌     |\n| View tests as hierarchy in IDE                     | ✅    | ❌    | ❌     |\n| Jump to nested `Describe` block via IDE            | ❌    | ❌    | ❌     |\n\n## Examples\n\nSee the [Oatmilk.Tests packages](./test/) for examples of how tests can be written. All tests for Oatmilk are written in it!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliammorrow%2Foatmilk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliammorrow%2Foatmilk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliammorrow%2Foatmilk/lists"}