Minicon.Assertions
1.3.0
dotnet add package Minicon.Assertions --version 1.3.0
NuGet\Install-Package Minicon.Assertions -Version 1.3.0
<PackageReference Include="Minicon.Assertions" Version="1.3.0" />
<PackageVersion Include="Minicon.Assertions" Version="1.3.0" />
<PackageReference Include="Minicon.Assertions" />
paket add Minicon.Assertions --version 1.3.0
#r "nuget: Minicon.Assertions, 1.3.0"
#:package Minicon.Assertions@1.3.0
#addin nuget:?package=Minicon.Assertions&version=1.3.0
#tool nuget:?package=Minicon.Assertions&version=1.3.0
Minicon.Assertions
Eine schlanke, kostenlose und MIT-lizenzierte Fluent-Assertion-Bibliothek für .NET im
vertrauten FluentAssertions-Stil (value.Should().Be(...)).
Hintergrund: FluentAssertions ist seit Version 8 kommerziell lizenzpflichtig.
Minicon.Assertionsbietet die wichtigsten Assertions als freie Alternative – ohne kommerzielle Einschränkungen, ohne Lizenzkosten.
Installation
dotnet add package Minicon.Assertions
Schnellstart
using Minicon.Assertions;
// Gleichheit
result.Should().Be(42);
name.Should().NotBe("falsch");
// Null
value.Should().NotBeNull();
optional.Should().BeNull();
// Begründung (because) – erscheint in der Fehlermeldung
order.Total.Should().Be(100, "die Summe der Positionen ergibt {0}", 100);
Funktioniert nahtlos mit xUnit, NUnit und MSTest: Fehlschläge werden automatisch
als die jeweils passende Test-Framework-Exception geworfen, damit Runner und IDE den
Fehler korrekt als fehlgeschlagene Assertion anzeigen. Ohne erkanntes Framework wird
MiniconAssertionException geworfen.
Unterstützte Assertions
Allgemein / Objekte
obj.Should().Be(expected);
obj.Should().NotBe(other);
obj.Should().BeNull();
obj.Should().NotBeNull();
obj.Should().BeSameAs(reference);
obj.Should().BeOfType<MyType>();
obj.Should().BeAssignableTo<IMyInterface>();
obj.Should().Match(x => x.Id > 0);
obj.Should().BeOneOf(a, b, c);
Strings
text.Should().Be("exakt");
text.Should().BeEquivalentTo("EXAKT"); // Groß-/Kleinschreibung egal
text.Should().Contain("teil");
text.Should().StartWith("Hallo");
text.Should().EndWith("Welt");
text.Should().HaveLength(11);
text.Should().BeEmpty();
text.Should().NotBeNullOrWhiteSpace();
Zahlen & vergleichbare Werte (int, long, double, decimal, DateTime, TimeSpan, …)
value.Should().BeGreaterThan(3);
value.Should().BeGreaterThanOrEqualTo(5);
value.Should().BeLessThan(10);
value.Should().BeInRange(1, 100);
value.Should().BePositive();
value.Should().BeNegative();
pi.Should().BeApproximately(3.14, 0.01); // Toleranz für Fließkomma
Nullable-Werte
int? x = 5;
x.Should().HaveValue();
x.Should().BeGreaterThan(3);
int? y = null;
y.Should().NotHaveValue();
Booleans
flag.Should().BeTrue();
flag.Should().BeFalse();
Collections
list.Should().HaveCount(3);
list.Should().BeEmpty();
list.Should().NotBeEmpty();
list.Should().Contain(item);
list.Should().Contain(x => x.IsActive);
list.Should().NotContain(item);
list.Should().ContainSingle();
list.Should().OnlyContain(x => x > 0);
list.Should().OnlyHaveUniqueItems();
list.Should().Equal(1, 2, 3); // gleiche Reihenfolge
list.Should().BeEquivalentTo(new[] {3, 2, 1}); // Reihenfolge egal
list.Should().ContainInOrder(1, 3);
Exceptions
Action act = () => service.Do();
act.Should().Throw<InvalidOperationException>()
.WithMessage("*Teil* der Meldung*"); // Wildcards: * und ?
act.Should().Throw<ArgumentException>()
.WithParameterName("documentId"); // ArgumentException.ParamName
act.Should().Throw<MyException>()
.Where(e => e.Code == 42); // Zusatzbedingung
act.Should().ThrowExactly<ArgumentException>(); // exakter Typ, keine Ableitung
act.Should().NotThrow();
act.Should().NotThrow<JsonException>();
// Func<T> (wertliefernd) – z.B. Konstruktoraufrufe
var create = () => new EmailAddress(raw);
create.Should().Throw<ArgumentException>();
// Komfort-Helfer
FluentActions.Invoking(() => service.Divide(1, 0))
.Should().Throw<DivideByZeroException>();
Async-Exceptions
Func<Task> act = () => service.DoAsync();
await act.Should().ThrowAsync<InvalidOperationException>()
.WithMessage("*fehlgeschlagen*"); // Verkettung ohne Zwischen-await
await act.Should().NotThrowAsync();
await FluentActions.Awaiting(() => service.DoAsync())
.Should().ThrowAsync<MyException>();
Objektgraph-Vergleich (BeEquivalentTo)
actual.Should().BeEquivalentTo(expected); // rekursiv, reihenfolgenunabhängig
list.Should().BeEquivalentTo(new[] { 3, 2, 1 }); // Collections
list.Should().BeEquivalentTo(expected, opt => opt.WithStrictOrdering());
Dictionaries
dict.Should().ContainKey("a");
dict.Should().ContainKey("a").WhoseValue.Should().Be(1);
dict.Should().NotContainKey("z");
dict.Should().Contain("key", "value");
Datum / Zeit
date.Should().BeAfter(start);
date.Should().BeOnOrBefore(deadline);
date.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(5));
Datums- und Zeitwerte fluent schreiben
Mit using Minicon.Assertions.Extensions; lassen sich Zeitpunkte und Zeitspannen
lesbar konstruieren – nicht nur in Assertions, sondern überall im Test:
using Minicon.Assertions.Extensions;
var options = new DistributedCacheEntryOptions
{
SlidingExpiration = 5.Seconds(),
AbsoluteExpiration = 1.January(2020).At(12, 34, 56).AsUtc(),
};
Datum: alle zwölf Monate als Erweiterung auf int
1.January(2020); // 01.01.2020 00:00:00
29.February(2020);
20.September(2018);
Uhrzeit – At(...) bis hinunter zu Nanosekunden (Vielfache von 100)
1.January(2020).At(12, 34); // 12:34:00
1.January(2020).At(12, 34, 56); // 12:34:56
1.January(2020).At(12, 34, 56, 123, 456, 700); // + ms, µs, ns
1.January(2020).At(new TimeSpan(12, 34, 56));
1.January(2020).At(new TimeOnly(12, 34, 56));
19.Hours().And(18.Minutes()).On(4.June(2016)); // umgekehrte Leserichtung
Kind & Offset – ändern nur die Interpretation, nicht die Uhrzeit
1.January(2020).At(12, 0).AsUtc(); // DateTimeKind.Utc
1.January(2020).At(12, 0).AsLocal(); // DateTimeKind.Local
1.January(2020).At(12, 0).AsUnspecified();
1.January(2020).At(12, 0).WithOffset(2.Hours()); // DateTimeOffset +02:00
1.January(2020).At(12, 0).ToDateTimeOffset(); // DateTimeOffset +00:00
1.January(2020).At(12, 0).AsDateOnly(); // DateOnly
1.January(2020).At(12, 0).AsTimeOnly(); // TimeOnly
Zeitspannen – auf int, long und double
5.Ticks(); 5.Microseconds(); 250.Milliseconds();
30.Seconds(); 5.Minutes(); 2.Hours(); 7.Days(); 2.Weeks();
1.5.Hours(); // auch gebrochen
2.Hours().And(30.Minutes()); // 02:30:00
2.Hours().Before(deadline); // DateTime/DateTimeOffset
1.Days().After(1.January(2020));
Die Erweiterungen liegen bewusst im eigenen Namespace Minicon.Assertions.Extensions,
damit int-Methoden wie Seconds() nur dort sichtbar sind, wo sie gewollt sind
(identisch zum Vorgehen von FluentAssertions.Extensions).
Typprüfungen & .Which
result.Should().BeOfType<Invoice>().Which.Total.Should().Be(100);
obj.Should().BeAssignableTo<IEntity>().Which.Id.Should().NotBe(0);
typeof(MyImpl).Should().BeAssignableTo<IMyInterface>(); // Type-Subjekt
Verkettung mit .And
result.Should().NotBeNull().And.Be("abc");
Migration von FluentAssertions
Die API ist bewusst kompatibel gehalten. In den meisten Fällen genügt es, das
using auszutauschen:
- using FluentAssertions;
+ using Minicon.Assertions;
- using FluentAssertions.Extensions;
+ using Minicon.Assertions.Extensions;
Nicht jede FluentAssertions-Methode ist (noch) abgedeckt – siehe Liste oben. Fehlt dir etwas? Pull Requests willkommen.
Build & Test
dotnet build
dotnet test
dotnet pack src/Minicon.Assertions/Minicon.Assertions.csproj -c Release
Lizenz
MIT – frei für kommerzielle und private Nutzung.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.