1 people like it.
Like the snippet!
Easy string approvals testing
This simple tool is useful for string based approvals testing. When tests are run in DEBUG mode, the code opens the p4merge diff tool (change it to point to yours) to clearly show the differences between the expected and actual strings. The actual, or received string is copied, in escaped form, to the clipboard so that it can be easily pasted into the associated test to approve the changes if appropriate.
When a debugger isn't attached, this tool reverts to using a standard Assert so that it won't attempt to open a diff viewer when executed on a CI server.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
|
module Assertions
open System
open System.IO
open System.Diagnostics
open System.Windows
open Microsoft.VisualStudio.TestTools.UnitTesting
/// Opens a set of files in a diff viewer.
/// Change to suit your diff viewer
let openDiff file1 file2 =
let startInfo = new ProcessStartInfo()
startInfo.FileName <- @"C:\Program Files\Perforce\p4merge.exe"
startInfo.Arguments <- sprintf "%s %s" file1 file2
Process.Start(startInfo);
/// Useable tempfile (see: http://fssnip.net/4N/)
type TempFile() =
let path = System.IO.Path.GetTempFileName()
member x.Path = path
interface System.IDisposable with
member x.Dispose() = System.IO.File.Delete(path)
/// Predicate for matching strings.
let stringsDiffer s1 s2 =
not (s1 = s2)
/// Uses a tester function to determine
/// if the given strings, s1 and s2 match.
let testStrings' testFails expected actual =
let diffMessage =
Environment.NewLine + "-------------------------" +
Environment.NewLine +
"This string has been copied to the clipboard." + Environment.NewLine +
"To approve, simply paste it into your test."
// Escape this to create a verabtim F# string that
// may be pasted directly into the test code.
let buildEscapedStringForClipboard (str:string) =
let doubleQuotesEscaped = str.Replace(@"""", @"""""")
sprintf @"@""%s""" doubleQuotesEscaped
let copyToClipboard str =
try
System.Windows.Clipboard.SetData(
DataFormats.Text,
buildEscapedStringForClipboard str);
with
| ex -> () // swallow any COM exceptions here. ...
// True if a debugger is attached to the
// process running the tests.
let debuggerIsAttached =
Debugger.IsAttached
if not debuggerIsAttached then
Assert.AreEqual(expected, actual) // Assume running on CI server.
else
if testFails expected actual
then // Show any change in diff viewer and copy received to clipboard.
use tempFileExpected = new TempFile()
use tempFileActual = new TempFile()
let tempFiles = [| tempFileExpected.Path; tempFileActual.Path |]
[| expected; actual + diffMessage |]
|> Array.zip tempFiles
|> Array.iter (fun tpl -> File.WriteAllText((fst tpl), (snd tpl)))
openDiff tempFileExpected.Path tempFileActual.Path |> ignore
actual |> copyToClipboard
failwith (sprintf "Expected <%s>, but actual is <%s>." expected actual)
/// Tests the given strings for equality.
/// If they're found to differ, then the
/// p4merge tools is opened to show the
/// differences. The received string is
/// escaped and sent to the clipboard
/// so that it may be easily copied to the
/// relvant test to accept the changes if
/// they're expected.
[<DebuggerStepThrough>]
let IsSameStringAs = testStrings' stringsDiffer
//--------------------------------------------------------
[<TestClass>]
type ``When Testing Using Strings``() =
// Sample unit test showing how to use the
// simple string approvals tool.
[<TestMethod>]
member this.``should be able to compare strings``() =
"Actual String" // created by unit under test.
|> IsSameStringAs @"Expected String"
|
module Assertions
namespace System
namespace System.IO
namespace System.Diagnostics
namespace System.Windows
namespace Microsoft
val openDiff : file1:string -> file2:string -> Process
Full name: Assertions.openDiff
Opens a set of files in a diff viewer.
Change to suit your diff viewer
val file1 : string
val file2 : string
val startInfo : ProcessStartInfo
Multiple items
type ProcessStartInfo =
new : unit -> ProcessStartInfo + 2 overloads
member Arguments : string with get, set
member CreateNoWindow : bool with get, set
member Domain : string with get, set
member EnvironmentVariables : StringDictionary
member ErrorDialog : bool with get, set
member ErrorDialogParentHandle : nativeint with get, set
member FileName : string with get, set
member LoadUserProfile : bool with get, set
member Password : SecureString with get, set
...
Full name: System.Diagnostics.ProcessStartInfo
--------------------
ProcessStartInfo() : unit
ProcessStartInfo(fileName: string) : unit
ProcessStartInfo(fileName: string, arguments: string) : unit
property ProcessStartInfo.FileName: string
property ProcessStartInfo.Arguments: string
val sprintf : format:Printf.StringFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
Multiple items
type Process =
inherit Component
new : unit -> Process
member BasePriority : int
member BeginErrorReadLine : unit -> unit
member BeginOutputReadLine : unit -> unit
member CancelErrorRead : unit -> unit
member CancelOutputRead : unit -> unit
member Close : unit -> unit
member CloseMainWindow : unit -> bool
member EnableRaisingEvents : bool with get, set
member ExitCode : int
...
Full name: System.Diagnostics.Process
--------------------
Process() : unit
Process.Start(startInfo: ProcessStartInfo) : Process
Process.Start(fileName: string) : Process
Process.Start(fileName: string, arguments: string) : Process
Process.Start(fileName: string, userName: string, password: Security.SecureString, domain: string) : Process
Process.Start(fileName: string, arguments: string, userName: string, password: Security.SecureString, domain: string) : Process
Multiple items
type TempFile =
interface IDisposable
new : unit -> TempFile
member Path : string
Full name: Assertions.TempFile
Useable tempfile (see: http://fssnip.net/4N/)
--------------------
new : unit -> TempFile
val path : string
type Path =
static val DirectorySeparatorChar : char
static val AltDirectorySeparatorChar : char
static val VolumeSeparatorChar : char
static val InvalidPathChars : char[]
static val PathSeparator : char
static member ChangeExtension : path:string * extension:string -> string
static member Combine : [<ParamArray>] paths:string[] -> string + 3 overloads
static member GetDirectoryName : path:string -> string
static member GetExtension : path:string -> string
static member GetFileName : path:string -> string
...
Full name: System.IO.Path
Path.GetTempFileName() : string
val x : TempFile
Multiple items
member TempFile.Path : string
Full name: Assertions.TempFile.Path
--------------------
type Path =
static val DirectorySeparatorChar : char
static val AltDirectorySeparatorChar : char
static val VolumeSeparatorChar : char
static val InvalidPathChars : char[]
static val PathSeparator : char
static member ChangeExtension : path:string * extension:string -> string
static member Combine : [<ParamArray>] paths:string[] -> string + 3 overloads
static member GetDirectoryName : path:string -> string
static member GetExtension : path:string -> string
static member GetFileName : path:string -> string
...
Full name: System.IO.Path
type IDisposable =
member Dispose : unit -> unit
Full name: System.IDisposable
override TempFile.Dispose : unit -> unit
Full name: Assertions.TempFile.Dispose
type File =
static member AppendAllLines : path:string * contents:IEnumerable<string> -> unit + 1 overload
static member AppendAllText : path:string * contents:string -> unit + 1 overload
static member AppendText : path:string -> StreamWriter
static member Copy : sourceFileName:string * destFileName:string -> unit + 1 overload
static member Create : path:string -> FileStream + 3 overloads
static member CreateText : path:string -> StreamWriter
static member Decrypt : path:string -> unit
static member Delete : path:string -> unit
static member Encrypt : path:string -> unit
static member Exists : path:string -> bool
...
Full name: System.IO.File
File.Delete(path: string) : unit
val stringsDiffer : s1:'a -> s2:'a -> bool (requires equality)
Full name: Assertions.stringsDiffer
Predicate for matching strings.
val s1 : 'a (requires equality)
val s2 : 'a (requires equality)
val not : value:bool -> bool
Full name: Microsoft.FSharp.Core.Operators.not
val testStrings' : testFails:(string -> string -> bool) -> expected:string -> actual:string -> unit
Full name: Assertions.testStrings'
Uses a tester function to determine
if the given strings, s1 and s2 match.
val testFails : (string -> string -> bool)
val expected : string
val actual : string
val diffMessage : string
type Environment =
static member CommandLine : string
static member CurrentDirectory : string with get, set
static member Exit : exitCode:int -> unit
static member ExitCode : int with get, set
static member ExpandEnvironmentVariables : name:string -> string
static member FailFast : message:string -> unit + 1 overload
static member GetCommandLineArgs : unit -> string[]
static member GetEnvironmentVariable : variable:string -> string + 1 overload
static member GetEnvironmentVariables : unit -> IDictionary + 1 overload
static member GetFolderPath : folder:SpecialFolder -> string + 1 overload
...
nested type SpecialFolder
nested type SpecialFolderOption
Full name: System.Environment
property Environment.NewLine: string
val buildEscapedStringForClipboard : (string -> string)
val str : string
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = String
Full name: Microsoft.FSharp.Core.string
val doubleQuotesEscaped : string
String.Replace(oldValue: string, newValue: string) : string
String.Replace(oldChar: char, newChar: char) : string
val copyToClipboard : ('a -> unit)
val str : 'a
namespace System.Text
val ex : exn
val debuggerIsAttached : bool
type Debugger =
new : unit -> Debugger
static val DefaultCategory : string
static member Break : unit -> unit
static member IsAttached : bool
static member IsLogging : unit -> bool
static member Launch : unit -> bool
static member Log : level:int * category:string * message:string -> unit
static member NotifyOfCrossThreadDependency : unit -> unit
Full name: System.Diagnostics.Debugger
property Debugger.IsAttached: bool
val tempFileExpected : TempFile
val tempFileActual : TempFile
val tempFiles : string []
property TempFile.Path: string
type Array =
member Clone : unit -> obj
member CopyTo : array:Array * index:int -> unit + 1 overload
member GetEnumerator : unit -> IEnumerator
member GetLength : dimension:int -> int
member GetLongLength : dimension:int -> int64
member GetLowerBound : dimension:int -> int
member GetUpperBound : dimension:int -> int
member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads
member Initialize : unit -> unit
member IsFixedSize : bool
...
Full name: System.Array
val zip : array1:'T1 [] -> array2:'T2 [] -> ('T1 * 'T2) []
Full name: Microsoft.FSharp.Collections.Array.zip
val iter : action:('T -> unit) -> array:'T [] -> unit
Full name: Microsoft.FSharp.Collections.Array.iter
val tpl : string * string
File.WriteAllText(path: string, contents: string) : unit
File.WriteAllText(path: string, contents: string, encoding: Text.Encoding) : unit
val fst : tuple:('T1 * 'T2) -> 'T1
Full name: Microsoft.FSharp.Core.Operators.fst
val snd : tuple:('T1 * 'T2) -> 'T2
Full name: Microsoft.FSharp.Core.Operators.snd
val ignore : value:'T -> unit
Full name: Microsoft.FSharp.Core.Operators.ignore
val failwith : message:string -> 'T
Full name: Microsoft.FSharp.Core.Operators.failwith
Multiple items
type DebuggerStepThroughAttribute =
inherit Attribute
new : unit -> DebuggerStepThroughAttribute
Full name: System.Diagnostics.DebuggerStepThroughAttribute
--------------------
DebuggerStepThroughAttribute() : unit
val IsSameStringAs : (string -> string -> unit)
Full name: Assertions.IsSameStringAs
Tests the given strings for equality.
If they're found to differ, then the
p4merge tools is opened to show the
differences. The received string is
escaped and sent to the clipboard
so that it may be easily copied to the
relvant test to accept the changes if
they're expected.
val this : When Testing Using Strings
member When Testing Using Strings.( should be able to compare strings ) : unit -> unit
Full name: Assertions.When Testing Using Strings.( should be able to compare strings )
More information