Edit
The function described below is available in the function repository, and can be used in version 11.3+ as, for example, ResourceFunction["WriteUnitTest"]["~/mytestfile.wl", 1/0 + 3]
Original answer
This answer gives a workflow using the "Testing Notebook" to create test files. I no longer use this workflow as I had encountered some instability with the testing notebooks as well as the fact that they do not deal with symbols outside of the Global and System contexts.
My current workflow is to write unit tests quickly in a normal notebook and have them written in a readable way to a file. I write the tests as soon as I get the functionality how I want it, I write the tests often and try to make them always self-contained.
Here is an example of a test for FindMoleculeSubstructure
. In a normal notebook I would evaluate
WriteUnitTest["MytestFile.mt",
With[{mol = Molecule[Interpreter["Chemical"]["buckyball"]],
fivememberedRing =
MoleculePattern[{_, _, _, _, _},
Bond /@ Partition[Range[5], 2, 1, 1]]},
Length[FindMoleculeSubstructure[mol, fivememberedRing, All]]]]
and I am presented with this dialog
when I press the "Yes" button the test is written to file in a very readable form. This is important because later when a test fails I don't want to waste any time staring at the test figuring out what it does.
The code for WriteUnitTest
is as follows:
SetAttributes[prettyInputString, HoldAllComplete];
prettyInputString[expr_] := Module[
{res},
res = GeneralUtilities`ToPrettyString[Unevaluated[expr]];
res // StringReplace["\r\n" -> "\n"] //
StringReplace["\n" -> "\n\t"] //
StringReplace["Global`" -> ""] //
StringReplace["\\[LeftDoubleBracket]"~~Shortest[part__]~~"\\[RightDoubleBracket]" :> "[["<>part<>"]]"]
]
Attributes[WriteUnitTest] = HoldRest
Options[WriteUnitTest] = {"ConfirmResults" -> True}
WriteUnitTest[filename_String, input_, append_String:"",opts:OptionsPattern[]] := DynamicModule[
{
inputString = prettyInputString[input], writeToFile,
output, outputString, newFile, evaluationData, messages, messageString,
fileText
},
evaluationData = EvaluationData[input];
output = evaluationData["Result"];
messages = evaluationData["MessagesExpressions"];
If[!FileExistsQ[filename],
newFile = True;
PrintTemporary["File to be created: ", filename],
PrintTemporary["File to be modified: ", filename]
];
outputString = With[{eval=output},prettyInputString[eval]];
(* the testing framework prefers HoldForm here *)
messageString = StringReplace[
ToString[messages,InputForm,PageWidth->80],
"Hold[" -> "HoldForm["
];
writeToFile[] := (
If[
newFile,
Export[
filename,
"BeginTestSection[\""<>FileBaseName[filename]<>"\"]\n\n\nEndTestSection[]\n",
"Text"
]
];
Block[{id = FileBaseName[filename]},
id = id <> "_" <> StringJoin[(If[# > 9, "", "0"] <>ToString[#]) & /@ Date[][[1;;3]]] <> "-" <> ToUpperCase[IntegerString[RandomInteger[{0, 36^^1000000}], 36, 6]] <> append;
Print["Adding test: ", id];
outputString = StringJoin[
"VerificationTest[\n\t",
inputString,
"\n\t,\n\t",
outputString,
"\n\t,\n\t",
messageString,
"\n\t,\n\tTestID->\"" <> id <> "\"\n]\n\n"
];
];
fileText = Import[filename, "Text"];
fileText = If[
StringContainsQ[
fileText,
"EndTestSection[]"
]
,
StringReplace[
fileText,
"EndTestSection[]" :> outputString <> "\nEndTestSection[]\n"
],
fileText<> "\n" <> outputString
];
Export[filename, fileText, "Text"];
Print @ filename;
SelectionMove[ButtonNotebook[], All, ButtonCell];
NotebookDelete[ButtonNotebook[]];
);
If[
OptionValue["ConfirmResults"]
,
Column[
{
Row[{"Input: ", inputString}],
Row[{"Result: ", outputString}],
Row[{"Messages: ", messageString}],
Row[
{
Button[
Text[Style["Yes"]],
writeToFile[];
,
"BaseStyle" -> Darker[Green]
]
,
Button[Text[Style["No"]],
Print["Aborting without writing to ", filename];
SelectionMove[ButtonNotebook[], All, ButtonCell];
NotebookDelete[ButtonNotebook[]];
, "BaseStyle" -> Darker[Red]]
},
" "
]
}
]
,
writeToFile[];
filename
]
]
It was hacked together, so any suggestions for improving it are most appreciated.
MUnit
tests here. UsingBeginTestSection[ "SectionName", $sectionSwitch ]
should enable one to turn off and on a section using the Boolean variable$sectionSwitch
. $\endgroup$BeginTestSection
does by doing?MUnit'BeginTestSection
. That functionality does not seem work in the newVerificationTest
framework. To me the function thus looks useless? $\endgroup$