{"id":20879048,"url":"https://github.com/netfabric/netfabric.assertive","last_synced_at":"2025-05-12T16:31:15.670Z","repository":{"id":48811959,"uuid":"211949793","full_name":"NetFabric/NetFabric.Assertive","owner":"NetFabric","description":"A fluent assertions library that performs full coverage on any enumerable type.","archived":false,"fork":false,"pushed_at":"2024-03-27T13:12:52.000Z","size":421,"stargazers_count":9,"open_issues_count":1,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-01T08:12:14.219Z","etag":null,"topics":["assertions","assertions-library","async-enumerable","coverage","csharp","dotnet","enumerable","enumerable-interfaces","enumerator-implementations","nuget-package","unit-testing"],"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/NetFabric.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}},"created_at":"2019-09-30T20:23:50.000Z","updated_at":"2023-09-11T01:15:07.000Z","dependencies_parsed_at":"2022-09-02T20:41:10.255Z","dependency_job_id":null,"html_url":"https://github.com/NetFabric/NetFabric.Assertive","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetFabric%2FNetFabric.Assertive","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetFabric%2FNetFabric.Assertive/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetFabric%2FNetFabric.Assertive/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetFabric%2FNetFabric.Assertive/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NetFabric","download_url":"https://codeload.github.com/NetFabric/NetFabric.Assertive/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253776796,"owners_count":21962557,"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":["assertions","assertions-library","async-enumerable","coverage","csharp","dotnet","enumerable","enumerable-interfaces","enumerator-implementations","nuget-package","unit-testing"],"created_at":"2024-11-18T07:15:03.715Z","updated_at":"2025-05-12T16:31:15.338Z","avatar_url":"https://github.com/NetFabric.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![GitHub last commit (master)](https://img.shields.io/github/last-commit/NetFabric/NetFabric.Assertive/master.svg?style=flat-square\u0026logo=github)](https://github.com/NetFabric/NetFabric.Assertive/commits/master)\n[![Build (master)](https://img.shields.io/github/workflow/status/NetFabric/NetFabric.Assertive/.NET%20Core/master.svg?style=flat-square\u0026logo=github)](https://github.com/NetFabric/NetFabric.Assertive/actions)\n[![Coverage](https://img.shields.io/coveralls/github/NetFabric/NetFabric.Assertive/master?style=flat-square\u0026logo=coveralls)](https://coveralls.io/github/NetFabric/NetFabric.Assertive)\n[![NuGet Version](https://img.shields.io/nuget/v/NetFabric.Assertive.svg?style=flat-square\u0026logo=nuget)](https://www.nuget.org/packages/NetFabric.Assertive/)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/NetFabric.Assertive.svg?style=flat-square\u0026logo=nuget)](https://www.nuget.org/packages/NetFabric.Assertive/) \n\n# NetFabric.Assertive\n\nThis is a assertions library that performs full coverage on most enumerable types and checks edge scenarios that many developers are not aware of.\n\n## Syntax\n\n```csharp\nsource.Must()\n    .BeNotNull()\n    .BeEnumerableOf\u003cint\u003e()\n    .BeEqualTo(new[] {0, 1, 2, 3, 4});\n```\n\n## Enumerables\n\nThis framework uses fluent syntax and the combination of the following methods allow the testing of any type of enumerable in a single assertion:\n\n- `BeEnumerableOf\u003cTActualItem\u003e()` - asserts that the type `TActual`, passed in to `Must\u003cTActual\u003e()`, is an enumerable that returns a stream of items of type `TActualItem`.\n\n- `BeAsyncEnumerableOf\u003cTActualItem\u003e()` - asserts that the type `TActual`, passed in to `Must\u003cTActual\u003e()`, is an asynchronous enumerable that returns a stream of items of type `TActualItem`.\n\n- `BeEqualTo\u003cTExpectedItem\u003e(IEnumerable\u003cTExpectedItem\u003e expected)` - asserts that the actual enumerable object contains the same items and in the same order as `expected`. It tests all the enumeration forms implemented by the type `TActual`, passed in to `Must\u003cTActual\u003e()`.\n\nCollections can have multiple forms of enumeration. For example; a collection that implements `IReadOnlyList\u003cT\u003e` can be enumerated using the indexer, using `IEnumerable\u003cT\u003e.GetEnumerator()`, using `IEnumerable.GetEnumerator()` and using a public `GetEnumerator()` that is not an override of any of these interfaces. There's no guarantee that they all are correctly implemented. The `Count` property can also return the wrong value.\n\n_NOTE: This project uses [NetFabric.CodeAnalysis](https://github.com/NetFabric/NetFabric.CodeAnalysis) to handle any kind of enumerable or async enumerable. Check its [README](https://github.com/NetFabric/NetFabric.CodeAnalysis/blob/master/README.md) for a detailed description._\n\nHere's an example of a collection with multiple possible enumerations and enumerator implementations:\n\n``` csharp\npublic readonly struct MyRange : IReadOnlyList\u003cint\u003e, IList\u003cint\u003e\n{    \n    public MyRange(int count)\n    {\n        Count = count;\n    }\n    \n    public readonly int Count { get; }\n    \n    public int this[int index]\n    {\n    \tget\n        {\n            if (index \u003c 0 || index \u003e= Count)\n                ThrowIndexOutOfRangeException();\n\n            return index;\n\n            static void ThrowIndexOutOfRangeException() =\u003e throw new IndexOutOfRangeException();\n        }\n    }\n \n    bool ICollection\u003cint\u003e.IsReadOnly =\u003e true;\n\n    public bool Contains(int item) =\u003e item \u003e= 0 \u0026\u0026 item \u003c Count;\n\n    public void CopyTo(int[] array, int arrayIndex)\n    {\n        for (var index = 0; index \u003c Count; index++)\n            array[index + arrayIndex] = index;\n    }\n\n    void ICollection\u003cint\u003e.Add(int item) =\u003e throw new NotSupportedException();\n    void ICollection\u003cint\u003e.Clear() =\u003e throw new NotSupportedException();\n    bool ICollection\u003cint\u003e.Remove(int item) =\u003e throw new NotSupportedException();\n    \n    int IList\u003cint\u003e.this[int index] \n    { \n        get =\u003e this[index]; \n        set =\u003e throw new NotSupportedException(); \n    }\n\n    public int IndexOf(int item) =\u003e item \u003e= 0 \u0026\u0026 item \u003c Count ? item : -1;\n\n    void IList\u003cint\u003e.Insert(int index, int item) =\u003e throw new NotSupportedException();\n    void IList\u003cint\u003e.RemoveAt(int index) =\u003e throw new NotSupportedException();\n    \n    public readonly Enumerator GetEnumerator() =\u003e new Enumerator(Count);\n    readonly IEnumerator\u003cint\u003e IEnumerable\u003cint\u003e.GetEnumerator() =\u003e new DisposableEnumerator(Count);\n    readonly IEnumerator IEnumerable.GetEnumerator() =\u003e new DisposableEnumerator(Count);\n    \n    public struct Enumerator\n    {\n        readonly int count;\n        int current;\n        \n        internal Enumerator(int count)\n        {\n            this.count = count;\n            current = -1;\n        }\n        \n        public readonly int Current =\u003e current;\n        \n        public bool MoveNext() =\u003e ++current \u003c count;\n    }\n    \n    class DisposableEnumerator : IEnumerator\u003cint\u003e\n    {\n        readonly int count;\n        int current;\n        \n        internal DisposableEnumerator(int count)\n        {\n            this.count = count;\n            current = -1;\n        }\n        \n        public int Current =\u003e current;\n        object IEnumerator.Current =\u003e current;\n        \n        public bool MoveNext() =\u003e ++current \u003c count;\n        \n        public void Reset() =\u003e current = -1;\n        \n        public void Dispose() {}\n    }\n}\n```\n\nThis example has two enumerators to: improve performance, allow casting to an enumerable interface and allow the use of extension methods for collections (like LINQ). The public enumerator is a value-type so that calls are not virtual. It doesn't implement `IDispose` so that, a `foreach` that calls it, can be inlined.\n\nIt implements `IReadOnlyCollection\u003c\u003e` as the number of items is known. It also implements `ICollection\u003c\u003e` so that it performs better when used with LINQ and when converted to an array or a `List\u003c\u003e`.\n\nIt implements `IReadOnlyList\u003c\u003e` so, the indexer can be used. The indexer performs much better that enumerators. It also implements `IList\u003c\u003e` so that it can be used on methods that still don't take `IReadOnlyList\u003c\u003e` parameters.\n\nOne single call to the `BeEnumerableOf\u003cTActualItem\u003e()` assertion validates if all implementations return the same sequence of items. Returned by the multiple `GetEnumerator()` methods, the `Count` property, the `CopyTo` method and the multiple indexers. It doesn't test `IndexOf()` and only partially tests `Contains()` methods because these would only work for certain sequences.\n\n### No enumerable interfaces\n\nA collection does not have to implement interfaces to be enumerable using `foreach` or `await foreach`. For example, these are two valid enumerables:\n\n``` csharp\npublic class Enumerable\u003cT\u003e\n{\n    public Enumerable\u003cT\u003e GetEnumerator() \n        =\u003e this;\n\n    public T Current \n        =\u003e default;\n\n    public bool MoveNext() \n        =\u003e default;\n}\n```\n\n``` csharp\npublic class AsyncEnumerable\u003cT\u003e\n{\n    public AsyncEnumerable\u003cT\u003e GetAsyncEnumerator() \n        =\u003e this;\n\n    public T Current \n        =\u003e default;\n\n    public ValueTask\u003cbool\u003e MoveNextAsync() \n        =\u003e default;\n}\n```\n\nTo be able to handle these, `NetFabric.Assertive` does not cast or constrain the collections to an enumerable interface. It uses [`NetFabric.Reflection`](https://www.nuget.org/packages/NetFabric.Reflection/) to validate if its an enumerable.\n\nEnumerables can also have `ref struct` enumerators. For example:\n\n``` csharp\nclass Enumerable\u003cT\u003e\n{\n    readonly Memory\u003cT\u003e source;\n    \n    public Enumerable(Memory\u003cT\u003e source)\n        =\u003e this.source = source;\n        \n    public Enumerator GetEnumerator()\n        =\u003e new(source);\n        \n    public ref struct Enumerator // ref struct enumerator\n    {\n        readonly Span\u003cT\u003e source;\n        int index;\n        \n        internal Enumerator(Memory\u003cT\u003e source)\n        {\n            this.source = source.Span;\n            index = -1;\n        }\n        \n        public T Current\n            =\u003e source[index];\n            \n        public bool MoveNext()\n            =\u003e ++index \u003c source.Length;\n    }\n}\n```\n\nThese cannot be boxed, so reflection cannot be used to enumerate these as it tries to cast them to `object`. To support these type of enumerables `NetFabric.Assertive` generates custom code using expression trees and the data collected by [`NetFabric.Reflection`](https://www.nuget.org/packages/NetFabric.Reflection/).\n\n`NetFabric.Assertive` also supports validation of async enumerables. To be able to enumerate these, its required the use of the `async` and `await` keywords so that the compiler generates a complex state machine that enumerates it. Expression trees do not yet support these. For this reason, `NetFabric.Assertive` uses a wrapper that implements `IAsyncEnumerable` and calls the enumerable using reflection.\n\n`await foreach` does not support `ref struct` enumerators so, the use of reflection in this case, is not an issue.\n\n## Limitations\n\n`foreach` and `await foreach` support the return by reference of items. Unfortunately reflection only supports this feature since `netstandard 2.1` and expression trees do not support it at all.\n\nFor these reasons, `NetFabric.Assertive` only support return by reference in the case of async enumerables and starting from `netstandard 2.1`.\n\nIn the case it's not supported, an exception is thrown. To be able to test the other types of enumeration on the enumerable, it's possible to disable this test by setting the optional parameter `warnRefReturns` to `false`:\n\n``` csharp\nresult.Must()\n    .BeEnumerableOf\u003cint\u003e()\n    .BeEqualTo(expected, warnRefReturns: false)\n```\n\nThis disables this particular test but leaves all the other enabled. You should still compare the enumeration using an alternative method. \n\n## References\n\n- [Enumeration in .NET](https://blog.usejournal.com/enumeration-in-net-d5674921512e) by Antão Almada\n- [Performance of value-type vs reference-type enumerators](https://medium.com/@antao.almada/performance-of-value-type-vs-reference-type-enumerators-820ab1acc291) by Antão Almada\n\n## Credits\n\nThe following open-source projects are used to build and test this project:\n\n- [.NET](https://github.com/dotnet)\n- [NetFabric.CodeAnalysis](https://github.com/NetFabric/NetFabric.CodeAnalysis)\n- [xUnit.net](https://xunit.net/)\n\n## License\n\nThis project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnetfabric%2Fnetfabric.assertive","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnetfabric%2Fnetfabric.assertive","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnetfabric%2Fnetfabric.assertive/lists"}