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