mirror of
https://github.com/Smaug123/WoofWare.Expect
synced 2025-10-05 20:48:40 +00:00
Add syntax for exceptions (#19)
This commit is contained in:
@@ -44,6 +44,13 @@ let ``This test fails: plain text comparison of ToString`` () =
|
|||||||
snapshot " 123 "
|
snapshot " 123 "
|
||||||
return 123
|
return 123
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``With return! and snapshotThrows, you can see exceptions too`` () =
|
||||||
|
expect {
|
||||||
|
snapshotThrows @"System.Exception: oh no"
|
||||||
|
return! (fun () -> failwith<int> "oh no")
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You can adjust the formatting:
|
You can adjust the formatting:
|
||||||
|
14
WoofWare.Expect.Test/TestExceptionThrowing.fs
Normal file
14
WoofWare.Expect.Test/TestExceptionThrowing.fs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
namespace WoofWare.Expect.Test
|
||||||
|
|
||||||
|
open NUnit.Framework
|
||||||
|
open WoofWare.Expect
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestExceptionThrowing =
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can throw an exception`` () =
|
||||||
|
expect {
|
||||||
|
snapshotThrows @"System.Exception: oh no"
|
||||||
|
return! (fun () -> failwith<int> "oh no")
|
||||||
|
}
|
@@ -13,6 +13,7 @@
|
|||||||
<Compile Include="Assembly.fs" />
|
<Compile Include="Assembly.fs" />
|
||||||
<Compile Include="BulkUpdateExample.fs" />
|
<Compile Include="BulkUpdateExample.fs" />
|
||||||
<Compile Include="SimpleTest.fs" />
|
<Compile Include="SimpleTest.fs" />
|
||||||
|
<Compile Include="TestExceptionThrowing.fs" />
|
||||||
<Compile Include="TestSurface.fs" />
|
<Compile Include="TestSurface.fs" />
|
||||||
<Compile Include="TestSnapshotFinding\TestSnapshotFinding.fs" />
|
<Compile Include="TestSnapshotFinding\TestSnapshotFinding.fs" />
|
||||||
<Compile Include="TestSnapshotFinding\TestUnicodeCharacters.fs" />
|
<Compile Include="TestSnapshotFinding\TestUnicodeCharacters.fs" />
|
||||||
|
@@ -142,6 +142,51 @@ type ExpectBuilder (mode : Mode) =
|
|||||||
Actual = None
|
Actual = None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Expresses that the given expression throws during evaluation.
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// expect {
|
||||||
|
/// snapshotThrows @"System.Exception: oh no"
|
||||||
|
/// return! (fun () -> failwith "oh no")
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
[<CustomOperation("snapshotThrows", MaintainsVariableSpaceUsingBind = true)>]
|
||||||
|
member _.SnapshotThrows<'a>
|
||||||
|
(
|
||||||
|
state : ExpectState<'a>,
|
||||||
|
snapshot : string,
|
||||||
|
[<CallerMemberName>] ?memberName : string,
|
||||||
|
[<CallerLineNumber>] ?callerLine : int,
|
||||||
|
[<CallerFilePath>] ?filePath : string
|
||||||
|
)
|
||||||
|
: ExpectState<'a>
|
||||||
|
=
|
||||||
|
match state.Snapshot with
|
||||||
|
| Some _ -> failwith "snapshot can only be specified once"
|
||||||
|
| None ->
|
||||||
|
|
||||||
|
let memberName = defaultArg memberName "<unknown method>"
|
||||||
|
let filePath = defaultArg filePath "<unknown file>"
|
||||||
|
let lineNumber = defaultArg callerLine -1
|
||||||
|
|
||||||
|
let callerInfo =
|
||||||
|
{
|
||||||
|
MemberName = memberName
|
||||||
|
FilePath = filePath
|
||||||
|
LineNumber = lineNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Formatter = None
|
||||||
|
JsonSerialiserOptions = state.JsonSerialiserOptions
|
||||||
|
JsonDocOptions = state.JsonDocOptions
|
||||||
|
Snapshot = Some (SnapshotValue.ThrowsException snapshot, callerInfo)
|
||||||
|
Actual = None
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Express that the <c>return</c> value of this builder should be formatted using this function, before
|
/// Express that the <c>return</c> value of this builder should be formatted using this function, before
|
||||||
/// comparing to the snapshot.
|
/// comparing to the snapshot.
|
||||||
@@ -156,7 +201,7 @@ type ExpectBuilder (mode : Mode) =
|
|||||||
| Some _ -> failwith "Please don't supply withFormat more than once"
|
| Some _ -> failwith "Please don't supply withFormat more than once"
|
||||||
| None ->
|
| None ->
|
||||||
{ state with
|
{ state with
|
||||||
Formatter = Some formatter
|
Formatter = Some (fun f -> f () |> formatter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -224,6 +269,17 @@ type ExpectBuilder (mode : Mode) =
|
|||||||
|
|
||||||
/// Expresses the "actual value" component of the assertion "expected snapshot = actual value".
|
/// Expresses the "actual value" component of the assertion "expected snapshot = actual value".
|
||||||
member _.Return (value : 'T) : ExpectState<'T> =
|
member _.Return (value : 'T) : ExpectState<'T> =
|
||||||
|
{
|
||||||
|
Snapshot = None
|
||||||
|
Formatter = None
|
||||||
|
JsonDocOptions = None
|
||||||
|
JsonSerialiserOptions = None
|
||||||
|
Actual = Some (fun () -> value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expresses the "actual value" component of the assertion "expected snapshot = actual value", but delayed behind
|
||||||
|
/// a function (by contrast with `Return`).
|
||||||
|
member _.ReturnFrom (value : unit -> 'T) : ExpectState<'T> =
|
||||||
{
|
{
|
||||||
Snapshot = None
|
Snapshot = None
|
||||||
Formatter = None
|
Formatter = None
|
||||||
|
@@ -17,20 +17,21 @@ type CallerInfo =
|
|||||||
type private SnapshotValue =
|
type private SnapshotValue =
|
||||||
| Json of expected : string
|
| Json of expected : string
|
||||||
| Formatted of expected : string
|
| Formatted of expected : string
|
||||||
|
| ThrowsException of expected : string
|
||||||
|
|
||||||
type private CompletedSnapshotValue<'T> =
|
type private CompletedSnapshotValue<'T> =
|
||||||
| Json of expected : string * JsonSerializerOptions * JsonDocumentOptions
|
| Json of expected : string * JsonSerializerOptions * JsonDocumentOptions
|
||||||
| Formatted of expected : string * format : ('T -> string)
|
| Formatted of expected : string * format : ((unit -> 'T) -> string)
|
||||||
|
|
||||||
/// The state accumulated by the `expect` builder. You should never find yourself interacting with this type.
|
/// The state accumulated by the `expect` builder. You should never find yourself interacting with this type.
|
||||||
type ExpectState<'T> =
|
type ExpectState<'T> =
|
||||||
private
|
private
|
||||||
{
|
{
|
||||||
Formatter : ('T -> string) option
|
Formatter : ((unit -> 'T) -> string) option
|
||||||
JsonSerialiserOptions : JsonSerializerOptions option
|
JsonSerialiserOptions : JsonSerializerOptions option
|
||||||
JsonDocOptions : JsonDocumentOptions option
|
JsonDocOptions : JsonDocumentOptions option
|
||||||
Snapshot : (SnapshotValue * CallerInfo) option
|
Snapshot : (SnapshotValue * CallerInfo) option
|
||||||
Actual : 'T option
|
Actual : (unit -> 'T) option
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The state accumulated by the `expect` builder. You should never find yourself interacting with this type.
|
/// The state accumulated by the `expect` builder. You should never find yourself interacting with this type.
|
||||||
@@ -39,7 +40,7 @@ type internal CompletedSnapshotGeneric<'T> =
|
|||||||
{
|
{
|
||||||
SnapshotValue : CompletedSnapshotValue<'T>
|
SnapshotValue : CompletedSnapshotValue<'T>
|
||||||
Caller : CallerInfo
|
Caller : CallerInfo
|
||||||
Actual : 'T
|
Actual : unit -> 'T
|
||||||
}
|
}
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
@@ -68,11 +69,22 @@ module internal CompletedSnapshotGeneric =
|
|||||||
| SnapshotValue.Formatted expected ->
|
| SnapshotValue.Formatted expected ->
|
||||||
let formatter =
|
let formatter =
|
||||||
match state.Formatter with
|
match state.Formatter with
|
||||||
| None -> fun x -> x.ToString ()
|
| None -> fun x -> x().ToString ()
|
||||||
| Some f -> f
|
| Some f -> f
|
||||||
|
|
||||||
CompletedSnapshotValue.Formatted (expected, formatter)
|
CompletedSnapshotValue.Formatted (expected, formatter)
|
||||||
|
|
||||||
|
| SnapshotValue.ThrowsException expected ->
|
||||||
|
CompletedSnapshotValue.Formatted (
|
||||||
|
expected,
|
||||||
|
fun x ->
|
||||||
|
try
|
||||||
|
x () |> ignore
|
||||||
|
"<no exception raised>"
|
||||||
|
with e ->
|
||||||
|
e.GetType().FullName + ": " + e.Message
|
||||||
|
)
|
||||||
|
|
||||||
{
|
{
|
||||||
SnapshotValue = snapshot
|
SnapshotValue = snapshot
|
||||||
Caller = source
|
Caller = source
|
||||||
@@ -84,7 +96,7 @@ module internal CompletedSnapshotGeneric =
|
|||||||
let internal replacement (s : CompletedSnapshotGeneric<'T>) =
|
let internal replacement (s : CompletedSnapshotGeneric<'T>) =
|
||||||
match s.SnapshotValue with
|
match s.SnapshotValue with
|
||||||
| CompletedSnapshotValue.Json (_existing, options, _) ->
|
| CompletedSnapshotValue.Json (_existing, options, _) ->
|
||||||
JsonSerializer.Serialize (s.Actual, options)
|
JsonSerializer.Serialize (s.Actual (), options)
|
||||||
|> JsonDocument.Parse
|
|> JsonDocument.Parse
|
||||||
|> _.RootElement
|
|> _.RootElement
|
||||||
|> _.ToString()
|
|> _.ToString()
|
||||||
@@ -104,7 +116,7 @@ module internal CompletedSnapshotGeneric =
|
|||||||
None
|
None
|
||||||
|
|
||||||
let canonicalActual =
|
let canonicalActual =
|
||||||
JsonSerializer.Serialize (state.Actual, jsonSerOptions) |> JsonDocument.Parse
|
JsonSerializer.Serialize (state.Actual (), jsonSerOptions) |> JsonDocument.Parse
|
||||||
|
|
||||||
match canonicalSnapshot with
|
match canonicalSnapshot with
|
||||||
| None -> Some ("[JSON failed to parse:] " + snapshot, canonicalActual.RootElement.ToString ())
|
| None -> Some ("[JSON failed to parse:] " + snapshot, canonicalActual.RootElement.ToString ())
|
||||||
|
@@ -147,7 +147,8 @@ module internal SnapshotUpdate =
|
|||||||
let searchText = String.concat "\n" relevantLines
|
let searchText = String.concat "\n" relevantLines
|
||||||
|
|
||||||
// Find snapshot keyword
|
// Find snapshot keyword
|
||||||
let snapshotMatch = Regex.Match (searchText, @"\b(snapshot|snapshotJson)\b")
|
let snapshotMatch =
|
||||||
|
Regex.Match (searchText, @"\b(snapshot|snapshotJson|snapshotThrows)\b")
|
||||||
|
|
||||||
if not snapshotMatch.Success then
|
if not snapshotMatch.Success then
|
||||||
None
|
None
|
||||||
|
@@ -16,9 +16,11 @@ WoofWare.Expect.ExpectBuilder.Bind [method]: ('U WoofWare.Expect.ExpectState, un
|
|||||||
WoofWare.Expect.ExpectBuilder.Delay [method]: (unit -> 'T WoofWare.Expect.ExpectState) -> (unit -> 'T WoofWare.Expect.ExpectState)
|
WoofWare.Expect.ExpectBuilder.Delay [method]: (unit -> 'T WoofWare.Expect.ExpectState) -> (unit -> 'T WoofWare.Expect.ExpectState)
|
||||||
WoofWare.Expect.ExpectBuilder.Return [method]: 'T -> 'T WoofWare.Expect.ExpectState
|
WoofWare.Expect.ExpectBuilder.Return [method]: 'T -> 'T WoofWare.Expect.ExpectState
|
||||||
WoofWare.Expect.ExpectBuilder.Return [method]: unit -> 'T WoofWare.Expect.ExpectState
|
WoofWare.Expect.ExpectBuilder.Return [method]: unit -> 'T WoofWare.Expect.ExpectState
|
||||||
|
WoofWare.Expect.ExpectBuilder.ReturnFrom [method]: (unit -> 'T) -> 'T WoofWare.Expect.ExpectState
|
||||||
WoofWare.Expect.ExpectBuilder.Run [method]: (unit -> 'T WoofWare.Expect.ExpectState) -> unit
|
WoofWare.Expect.ExpectBuilder.Run [method]: (unit -> 'T WoofWare.Expect.ExpectState) -> unit
|
||||||
WoofWare.Expect.ExpectBuilder.Snapshot [method]: ('a WoofWare.Expect.ExpectState, string, string option, int option, string option) -> 'a WoofWare.Expect.ExpectState
|
WoofWare.Expect.ExpectBuilder.Snapshot [method]: ('a WoofWare.Expect.ExpectState, string, string option, int option, string option) -> 'a WoofWare.Expect.ExpectState
|
||||||
WoofWare.Expect.ExpectBuilder.SnapshotJson [method]: (unit WoofWare.Expect.ExpectState, string, string option, int option, string option) -> 'a WoofWare.Expect.ExpectState
|
WoofWare.Expect.ExpectBuilder.SnapshotJson [method]: (unit WoofWare.Expect.ExpectState, string, string option, int option, string option) -> 'a WoofWare.Expect.ExpectState
|
||||||
|
WoofWare.Expect.ExpectBuilder.SnapshotThrows [method]: ('a WoofWare.Expect.ExpectState, string, string option, int option, string option) -> 'a WoofWare.Expect.ExpectState
|
||||||
WoofWare.Expect.ExpectBuilder.WithFormat [method]: ('T WoofWare.Expect.ExpectState, 'T -> string) -> 'T WoofWare.Expect.ExpectState
|
WoofWare.Expect.ExpectBuilder.WithFormat [method]: ('T WoofWare.Expect.ExpectState, 'T -> string) -> 'T WoofWare.Expect.ExpectState
|
||||||
WoofWare.Expect.ExpectBuilder.WithJsonDocOptions [method]: ('T WoofWare.Expect.ExpectState, System.Text.Json.JsonDocumentOptions) -> 'T WoofWare.Expect.ExpectState
|
WoofWare.Expect.ExpectBuilder.WithJsonDocOptions [method]: ('T WoofWare.Expect.ExpectState, System.Text.Json.JsonDocumentOptions) -> 'T WoofWare.Expect.ExpectState
|
||||||
WoofWare.Expect.ExpectBuilder.WithJsonSerializerOptions [method]: ('T WoofWare.Expect.ExpectState, System.Text.Json.JsonSerializerOptions) -> 'T WoofWare.Expect.ExpectState
|
WoofWare.Expect.ExpectBuilder.WithJsonSerializerOptions [method]: ('T WoofWare.Expect.ExpectState, System.Text.Json.JsonSerializerOptions) -> 'T WoofWare.Expect.ExpectState
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "0.4",
|
"version": "0.5",
|
||||||
"publicReleaseRefSpec": [
|
"publicReleaseRefSpec": [
|
||||||
"^refs/heads/main$"
|
"^refs/heads/main$"
|
||||||
],
|
],
|
||||||
@@ -10,4 +10,4 @@
|
|||||||
":/Directory.Build.props",
|
":/Directory.Build.props",
|
||||||
":/LICENSE"
|
":/LICENSE"
|
||||||
]
|
]
|
||||||
}
|
}
|
Reference in New Issue
Block a user