diff --git a/docs/FsUnitTyped.fsx b/docs/FsUnitTyped.fsx index fa8694fa..11f035e7 100644 --- a/docs/FsUnitTyped.fsx +++ b/docs/FsUnitTyped.fsx @@ -9,9 +9,9 @@ What is FsUnitTyped? =============== **FsUnitTyped** is a statically typed set of FsUnit operators that makes -unit-testing with `FsUnit` even more safe and enjoyable (Available only for `NUnit`). +unit-testing with `FsUnit` even more safe and enjoyable (Available only for `NUnit` and `Xunit`). -No more untyped constrains and tests like +No more untyped constraints and tests like 1 |> should equal "1" diff --git a/src/FsUnit.Xunit/FsUnit.Xunit.fsproj b/src/FsUnit.Xunit/FsUnit.Xunit.fsproj index ccc25ccf..9ad97a92 100644 --- a/src/FsUnit.Xunit/FsUnit.Xunit.fsproj +++ b/src/FsUnit.Xunit/FsUnit.Xunit.fsproj @@ -14,6 +14,7 @@ + @@ -22,4 +23,4 @@ - \ No newline at end of file + diff --git a/src/FsUnit.Xunit/FsUnitTyped.fs b/src/FsUnit.Xunit/FsUnitTyped.fs new file mode 100644 index 00000000..178e1e36 --- /dev/null +++ b/src/FsUnit.Xunit/FsUnitTyped.fs @@ -0,0 +1,62 @@ +namespace FsUnitTyped + +open System.Diagnostics +open Xunit +open FsUnit.Xunit + +[] +module TopLevelOperators = + + /// Asserts that `expected` is equal to `actual`. + /// The equality instance on `actual` is used, if available. + [] + let shouldEqual<'a> (expected: 'a) (actual: 'a) = + actual |> should equal expected + + /// Asserts that `expected` is not equal to `actual`. + /// The equality instance on `actual` is used, if available. + [] + let shouldNotEqual<'a> (expected: 'a) (actual: 'a) = + actual |> should not' (equal expected) + + [] + let shouldContain<'a when 'a: equality> (expected: 'a) (actual: 'a seq) = + actual |> should contain expected + + [] + let shouldBeEmpty<'a>(actual: 'a seq) = + Assert.Empty actual + + [] + let shouldNotContain<'a when 'a: equality> (expected: 'a) (actual: 'a seq) = + if Seq.exists ((=) expected) actual then + failwith $"Seq %A{actual} should not contain %A{expected}" + + [] + let shouldBeSmallerThan<'a when 'a: comparison> (expected: 'a) (actual: 'a) = + actual |> should be (lessThan expected) + + [] + let shouldBeGreaterThan<'a when 'a: comparison> (expected: 'a) (actual: 'a) = + actual |> should be (greaterThan expected) + + [] + let shouldFail<'exn when 'exn :> exn>(f: unit -> unit) = + f |> should throw typeof<'exn> + + [] + let shouldContainText (expected: string) (actual: string) = + if actual.Contains(expected) |> not then + failwith $"\"{expected}\" is not a substring of \"{actual}\"" + + [] + let shouldNotContainText (expected: string) (actual: string) = + if actual.Contains(expected) then + failwith $"\"{expected}\" is a substring of \"{actual}\"" + + [] + let shouldHaveLength<'a> (expected: int) (actual: 'a seq) = + let actual = Seq.length actual + + if actual <> expected then + failwith $"Invalid length in %A{actual}\r\nExpected: {expected}\r\nActual: {actual}" diff --git a/tests/FsUnit.Xunit.Test/FsUnit.Xunit.Test.fsproj b/tests/FsUnit.Xunit.Test/FsUnit.Xunit.Test.fsproj index 50895d0b..e4579bb1 100644 --- a/tests/FsUnit.Xunit.Test/FsUnit.Xunit.Test.fsproj +++ b/tests/FsUnit.Xunit.Test/FsUnit.Xunit.Test.fsproj @@ -36,10 +36,19 @@ + + + + + + + + + - \ No newline at end of file + diff --git a/tests/FsUnit.Xunit.Test/equalTests.fs b/tests/FsUnit.Xunit.Test/equalTests.fs index e27684c7..2343e4a6 100644 --- a/tests/FsUnit.Xunit.Test/equalTests.fs +++ b/tests/FsUnit.Xunit.Test/equalTests.fs @@ -61,6 +61,10 @@ type ``equal Tests``() = member __.``reference type should fail to not equal itself``() = anObj |> should equal anObj + [] + member __.``should pass when Equals returns true``() = + anObj |> should equal (box(new AlwaysEqual())) + [] member __.``should fail when Equals returns false``() = anObj |> should not' (equal(NeverEqual())) @@ -69,6 +73,10 @@ type ``equal Tests``() = member __.``should pass when negated and Equals returns false``() = anObj |> should not' (equal(NeverEqual())) + [] + member __.``should fail when negated and Equals returns true``() = + shouldFail(fun () -> anObj |> should not' (equal(box(AlwaysEqual())))) + [] member __.``should pass when comparing two lists that have the same values``() = [ 1 ] |> should equal [ 1 ] diff --git a/tests/FsUnit.Xunit.Test/shouldFailTests.fs b/tests/FsUnit.Xunit.Test/shouldFailTests.fs index c3d769d2..5dca8338 100644 --- a/tests/FsUnit.Xunit.Test/shouldFailTests.fs +++ b/tests/FsUnit.Xunit.Test/shouldFailTests.fs @@ -17,7 +17,7 @@ type ``shouldFail tests``() = shouldFail(fun () -> shouldFail id) [] - member __.``shouldFaild should throw an exception``() = + member __.``shouldFail should throw an exception``() = (fun () -> shouldFail id) |> should throw typeof [] diff --git a/tests/FsUnit.Xunit.Test/typed.beEmptyTests.fs b/tests/FsUnit.Xunit.Test/typed.beEmptyTests.fs new file mode 100644 index 00000000..3ea80fa4 --- /dev/null +++ b/tests/FsUnit.Xunit.Test/typed.beEmptyTests.fs @@ -0,0 +1,29 @@ +namespace FsUnit.Typed.Test + +open Xunit +open FsUnitTyped + +type ``shouldBeEmpty tests``() = + [] + member __.``empty List should be Empty``() = + [] |> shouldBeEmpty + + [] + member __.``non-empty List should fail to be Empty``() = + shouldFail(fun () -> [ 1 ] |> shouldBeEmpty) + + [] + member __.``empty Array should be Empty``() = + [||] |> shouldBeEmpty + + [] + member __.``non-empty Array should fail to be Empty``() = + shouldFail(fun () -> [| 1 |] |> shouldBeEmpty) + + [] + member __.``empty Seq should be Empty``() = + Seq.empty |> shouldBeEmpty + + [] + member __.``non-empty Seq should fail to be Empty``() = + shouldFail(fun () -> seq { 1 } |> shouldBeEmpty) diff --git a/tests/FsUnit.Xunit.Test/typed.haveLengthTests.fs b/tests/FsUnit.Xunit.Test/typed.haveLengthTests.fs new file mode 100644 index 00000000..1e8228b2 --- /dev/null +++ b/tests/FsUnit.Xunit.Test/typed.haveLengthTests.fs @@ -0,0 +1,23 @@ +namespace FsUnit.Typed.Test + +open Xunit +open FsUnitTyped + +type ``haveLength tests``() = + // F# List + [] + member __.``List with 1 item should have Length 1``() = + [ 1 ] |> shouldHaveLength 1 + + [] + member __.``empty List should fail to have Length 1``() = + shouldFail(fun () -> [] |> shouldHaveLength 1) + + // Array + [] + member __.``Array with 1 item should have Length 1``() = + [| 1 |] |> shouldHaveLength 1 + + [] + member __.``empty Array should fail to have Length 1``() = + shouldFail(fun () -> [||] |> shouldHaveLength 1) diff --git a/tests/FsUnit.Xunit.Test/typed.shouldBeGreaterThanTests.fs b/tests/FsUnit.Xunit.Test/typed.shouldBeGreaterThanTests.fs new file mode 100644 index 00000000..7f1a742a --- /dev/null +++ b/tests/FsUnit.Xunit.Test/typed.shouldBeGreaterThanTests.fs @@ -0,0 +1,13 @@ +namespace FsUnit.Typed.Test + +open Xunit +open FsUnitTyped + +type ``shouldBeGreaterThan tests``() = + [] + member __.``11 should be greater than 10``() = + 11 |> shouldBeGreaterThan 10 + + [] + member __.``11[dot]1 should be greater than 11[dot]0``() = + 11.1 |> shouldBeGreaterThan 11.0 diff --git a/tests/FsUnit.Xunit.Test/typed.shouldBeSmallerThanTests.fs b/tests/FsUnit.Xunit.Test/typed.shouldBeSmallerThanTests.fs new file mode 100644 index 00000000..53c55641 --- /dev/null +++ b/tests/FsUnit.Xunit.Test/typed.shouldBeSmallerThanTests.fs @@ -0,0 +1,24 @@ +namespace FsUnit.Typed.Test + +open FsUnit.Xunit +open Xunit +open FsUnitTyped + +type ``shouldBeSmallerThan tests``() = + [] + member __.``10 should be less than 11``() = + 10 |> shouldBeSmallerThan 11 + + [] + member __.``10 should not be less than 10``() = + (fun () -> 10 |> shouldBeSmallerThan 10) |> shouldFail + + [] + member __.``10[dot]0 should be less than 10[dot]1``() = + 10.0 |> shouldBeSmallerThan 10.1 + + [] + member __.``10[dot]0 should not be less than 10[dot]0``() = + (fun () -> 10.0 |> shouldBeSmallerThan 10.0) + |> should throw typeof +//|> shouldFail diff --git a/tests/FsUnit.Xunit.Test/typed.shouldContainTests.fs b/tests/FsUnit.Xunit.Test/typed.shouldContainTests.fs new file mode 100644 index 00000000..baa4b442 --- /dev/null +++ b/tests/FsUnit.Xunit.Test/typed.shouldContainTests.fs @@ -0,0 +1,53 @@ +namespace FsUnit.Typed.Test + +open Xunit +open FsUnitTyped + +type ``shouldContain tests``() = + [] + member __.``List with item should contain item``() = + [ 1 ] |> shouldContain 1 + + [] + member __.``empty List should fail to contain item``() = + shouldFail(fun () -> [] |> shouldContain 1) + + [] + member __.``empty List should not contain item``() = + [] |> shouldNotContain 1 + + [] + member __.``List with item should fail to not contain item``() = + shouldFail(fun () -> [ 1 ] |> shouldNotContain 1) + + [] + member __.``Array with item should contain item``() = + [| 1 |] |> shouldContain 1 + + [] + member __.``empty Array should fail to contain item``() = + shouldFail(fun () -> [||] |> shouldContain 1) + + [] + member __.``empty Array should not contain item``() = + [||] |> shouldNotContain 1 + + [] + member __.``Array with item should fail to not contain item``() = + shouldFail(fun () -> [| 1 |] |> shouldNotContain 1) + + [] + member __.``Seq with item should contain item``() = + seq { 1 } |> shouldContain 1 + + [] + member __.``empty Seq should fail to contain item``() = + shouldFail(fun () -> Seq.empty |> shouldContain 1) + + [] + member __.``empty Seq should not contain item``() = + Seq.empty |> shouldNotContain 1 + + [] + member __.``Seq with item should fail to not contain item``() = + shouldFail(fun () -> seq { 1 } |> shouldNotContain 1) diff --git a/tests/FsUnit.Xunit.Test/typed.shouldContainText.fs b/tests/FsUnit.Xunit.Test/typed.shouldContainText.fs new file mode 100644 index 00000000..5e377602 --- /dev/null +++ b/tests/FsUnit.Xunit.Test/typed.shouldContainText.fs @@ -0,0 +1,13 @@ +namespace FsUnit.Typed.Test + +open Xunit +open FsUnitTyped + +type ``shouldContainText tests``() = + [] + member __.``empty string should contain ""``() = + "" |> shouldContainText "" + + [] + member __.``ships should contain hip``() = + "ships" |> shouldContainText "hip" diff --git a/tests/FsUnit.Xunit.Test/typed.shouldEqualNullTests.fs b/tests/FsUnit.Xunit.Test/typed.shouldEqualNullTests.fs new file mode 100644 index 00000000..4c294380 --- /dev/null +++ b/tests/FsUnit.Xunit.Test/typed.shouldEqualNullTests.fs @@ -0,0 +1,13 @@ +namespace FsUnit.Typed.Test + +open Xunit +open FsUnitTyped + +type ``Typed: shouldEqual null tests``() = + [] + member __.``null should be null``() = + null |> shouldEqual null + + [] + member __.``null should fail to not be null``() = + shouldFail(fun () -> null |> shouldNotEqual null) diff --git a/tests/FsUnit.Xunit.Test/typed.shouldEqualTests.fs b/tests/FsUnit.Xunit.Test/typed.shouldEqualTests.fs new file mode 100644 index 00000000..6606fa25 --- /dev/null +++ b/tests/FsUnit.Xunit.Test/typed.shouldEqualTests.fs @@ -0,0 +1,138 @@ +namespace FsUnit.Typed.Test + +open System.Collections.Immutable + +open FsUnit.Xunit +open Xunit +open FsUnitTyped +open System + +type AlwaysEqual() = + override __.Equals(other) = true + override __.GetHashCode() = 1 + +type NeverEqual() = + override __.Equals(other) = false + override __.GetHashCode() = 1 + +type ``shouldEqual Tests``() = + let anObj = new obj() + let otherObj = new obj() + let anImmutableArray = ImmutableArray.Create(1, 2, 3) + let equivalentImmutableArray = ImmutableArray.Create(1, 2, 3) + let otherImmutableArray = ImmutableArray.Create(1, 2, 4) + + [] + member __.``value type should equal equivalent value``() = + 1 |> shouldEqual 1 + + [] + member __.``value type should fail to equal nonequivalent value``() = + shouldFail(fun () -> 1 |> shouldEqual 2) + + [] + member __.``value type should not equal nonequivalent value``() = + 1 |> shouldNotEqual 2 + + [] + member __.``value type should fail to not equal equivalent value``() = + shouldFail(fun () -> 1 |> shouldNotEqual 1) + + [] + member __.``reference type should equal itself``() = + anObj |> shouldEqual anObj + + [] + member __.``reference type should fail to equal other``() = + shouldFail(fun () -> anObj |> shouldEqual otherObj) + + [] + member __.``reference type should not equal other``() = + anObj |> shouldNotEqual otherObj + + [] + member __.``reference type should fail to not equal itself``() = + shouldFail(fun () -> anObj |> shouldNotEqual anObj) + + [] + member __.``should pass when Equals returns true``() = + anObj |> shouldEqual(box(AlwaysEqual())) + + [] + member __.``should fail when Equals returns false``() = + shouldFail(fun () -> anObj |> shouldEqual(box(new NeverEqual()))) + + [] + member __.``should pass when negated and Equals returns false``() = + anObj |> shouldNotEqual(box(new NeverEqual())) + + [] + member __.``should fail when negated and Equals returns true``() = + shouldFail(fun () -> anObj |> shouldNotEqual(box(AlwaysEqual()))) + + [] + member __.``None should equal None``() = + None |> shouldEqual None + + [] + member __.``Error "Foo" should equal Error "Foo"``() = + Error "Foo" |> shouldEqual(Error "Foo") + + [] + member __.``Error "Foo" should equal fails and have same message``() = + (fun () -> Error "Foo" |> shouldEqual(Error "Bar")) + |> Assert.Throws + |> fun e -> + e.Message + |> shouldEqual( + sprintf + "Exception of type 'FsUnit.Xunit+MatchException' was thrown.%sExpected: Equals Error \"Bar\"%sActual: Error \"Foo\"" + Environment.NewLine + Environment.NewLine + ) + + [] + member __.``Error "Foo" should not equal Error "Bar"``() = + Error "Foo" |> shouldNotEqual(Error "Bar") + + [] + member __.``Error "Foo" should not equal Error "Bar" fails and have same message``() = + (fun () -> Error "Foo" |> shouldNotEqual(Error "Foo")) + |> Assert.Throws + |> fun e -> + e.Message + |> shouldEqual( + sprintf + "Exception of type 'FsUnit.Xunit+MatchException' was thrown.%sExpected: not Equals Error \"Foo\"%sActual: Error \"Foo\"" + Environment.NewLine + Environment.NewLine + ) + + [] + member this.``structural equality``() = + let actualList: char list = [] + [ (actualList, "") ] |> shouldEqual [ ([], "") ] + + [] + member __.``Empty obj list should match itself``() = + [] |> shouldEqual [] + + [] + member __.``List with elements should not match empty list``() = + [ 1 ] |> shouldNotEqual [] + + [] + member __.``structural value type should equal equivalent value``() = + anImmutableArray |> shouldEqual equivalentImmutableArray + + [] + member __.``structural value type should not equal non-equivalent value``() = + anImmutableArray |> shouldNotEqual otherImmutableArray + + [] + member __.``structural comparable type containing non-equivalent structural equatable type fails with correct exception``() = + let array1 = ImmutableArray.Create(Uri("https://example.com/1")) + + let array2 = ImmutableArray.Create(Uri("https://example.com/2")) + + shouldFail(fun () -> array1 |> shouldEqual array2) diff --git a/tests/FsUnit.Xunit.Test/typed.shouldFailTests.fs b/tests/FsUnit.Xunit.Test/typed.shouldFailTests.fs new file mode 100644 index 00000000..7bc9636a --- /dev/null +++ b/tests/FsUnit.Xunit.Test/typed.shouldFailTests.fs @@ -0,0 +1,21 @@ +namespace FsUnit.Typed.Test + +open Xunit +open FsUnitTyped + +type ``shouldFail tests``() = + [] + member __.``empty List should fail to contain item``() = + shouldFail(fun () -> [] |> shouldContain 1) + + [] + member __.``non-null should fail to be Null``() = + shouldFail(fun () -> "something" |> shouldEqual null) + + [] + member __.``shouldFail should fail when everything is OK``() = + shouldFail(fun () -> shouldFail id) + + [] + member __.``Simplify "should throw"``() = + (fun () -> failwith "BOOM!") |> shouldFail