I had a requirement to include unit testing in a project I'm working on, so created this UnitTest module which allows you to create a unit test file within your application and create tests (procedures) which consist of assertion tests, providing a nice readable output (as well as providing an exit code of 0 on success, or -1 on test(s) failing for automation purposes)
Assertion Methods:
Code: Select all
UnitTest::Assert(Expression, Description$) ;- Generic expression assertion test (i.e. #True = #True)
UnitTest::AssertTrue(Expression, Description$) ;- Test the expression is true.
UnitTest::AssertFalse(Expression, Description$) ;- Test the expression is false.
UnitTest::AssertEqual(LH, RH, Description$) ;- Test that the Left Hand variable equals Right Hand variable.
UnitTest::AssertNotEqual(LH, RH, Description$) ;- Test that the Left Hand variable does not equal Right Hand variable.
UnitTest::AssertGreater(LH, RH, Description$) ;- Test that the Left Hand variable is greater than the Right Hand variable.
UnitTest::AssertLess(LH, RH, Description$) ;- Test that the Left Hand variable is less than the Right Hand variable.
UnitTest::AssertEmpty(String, Description$) ;- Test that the passed String is empty (Len=0)
UnitTest::AssertNotEmpty(String, Description$) ;- Test that the passed String is not empty (Len>0)
Code: Select all
;- Create a Test
Procedure MyTest()
; Perform Assertions
Endprocedure : UnitTest::Register(MyTest)
Executing:
Code: Select all
;- Will run the unit tests in the queue and quit the application.
UnitTest::RunAll(#True) ; Pass True if you want the console to pause (input()) on exit.
;- If you want a quick way to only run if the file is the main file;
UnitTest::RunIfMain(#True)

Code (with Sample)
Code: Select all
CompilerIf Defined(PreComp, #PB_Module) = #False
DeclareModule PreComp
Macro _DQuote
"
EndMacro
Macro _Str(Expression)
PreComp::_DQuote#Expression#PreComp::_DQuote
EndMacro
EndDeclareModule
Module PreComp : EndModule
CompilerEndIf
DeclareModule UnitTest
Prototype pTestHandler()
Declare _registerHandler(TestName.s, TestFile.s, *TestHandler.pTestHandler)
Declare _handleAssert(AssertType.s, Description.s, Result, Test.s, FailReason.s = "")
Declare RunAll(PauseOnExit = #False)
Macro RunIfMain(PauseOnExit = #False)
CompilerIf #PB_Compiler_IsMainFile
UnitTest::RunAll(PauseOnExit)
CompilerEndIf
EndMacro
Macro Assert(Expression, Description)
If Expression
UnitTest::_handleAssert("Assert", Description, #True, #PB_Compiler_Procedure)
Else
UnitTest::_handleAssert("Assert", Description, #False, #PB_Compiler_Procedure)
EndIf
EndMacro
Macro AssertTrue(Expression, Description)
If (Expression) = #True
UnitTest::_handleAssert("AssertTrue", Description, #True, #PB_Compiler_Procedure)
Else
UnitTest::_handleAssert("AssertTrue", Description, #False, #PB_Compiler_Procedure)
EndIf
EndMacro
Macro AssertFalse(Expression, Description)
If (Expression) = #False
UnitTest::_handleAssert("AssertFalse", Description, #True, #PB_Compiler_Procedure)
Else
UnitTest::_handleAssert("AssertFalse", Description, #False, #PB_Compiler_Procedure)
EndIf
EndMacro
Macro AssertEqual(LH, RH, Description)
CompilerIf TypeOf(LH) <> #PB_String
If LH = RH
UnitTest::_handleAssert("AssertEqual", Description, #True, #PB_Compiler_Procedure)
Else
UnitTest::_handleAssert("AssertEqual", Description, #False, #PB_Compiler_Procedure, Str(LH) + " != " + Str(RH))
EndIf
CompilerElse
If LH = RH
UnitTest::_handleAssert("AssertEqual", Description, #True, #PB_Compiler_Procedure)
Else
UnitTest::_handleAssert("AssertEqual", Description, #False, #PB_Compiler_Procedure, "Strings not Equal")
EndIf
CompilerEndIf
EndMacro
Macro AssertNotEqual(LH, RH, Description)
CompilerIf TypeOf(LH) <> #PB_String
If LH <> RH
UnitTest::_handleAssert("AssertNotEqual", Description, #True, #PB_Compiler_Procedure)
Else
UnitTest::_handleAssert("AssertNotEqual", Description, #False, #PB_Compiler_Procedure, Str(LH) + " = " + Str(RH))
EndIf
CompilerElse
If LH <> RH
UnitTest::_handleAssert("AssertNotEqual", Description, #True, #PB_Compiler_Procedure)
Else
UnitTest::_handleAssert("AssertNotEqual", Description, #False, #PB_Compiler_Procedure, "Strings Equal")
EndIf
CompilerEndIf
EndMacro
Macro AssertGreater(LH, RH, Description)
CompilerIf TypeOf(LH) <> #PB_String
If Bool(LH > RH)
UnitTest::_handleAssert("AssertGreater", Description, #True, #PB_Compiler_Procedure)
Else
UnitTest::_handleAssert("AssertGreater", Description, #False, #PB_Compiler_Procedure, Str(LH) + " not greater than " + Str(RH))
EndIf
CompilerElse
UnitTest::_handleAssert("AssertGreater", Description, #False, #PB_Compiler_Procedure, "Cannot compare string")
CompilerEndIf
EndMacro
Macro AssertLess(LH, RH, Description)
CompilerIf TypeOf(LH) <> #PB_String
If Bool(LH < RH)
UnitTest::_handleAssert("AssertLess", Description, #True, #PB_Compiler_Procedure)
Else
UnitTest::_handleAssert("AssertLess", Description, #False, #PB_Compiler_Procedure, Str(LH) + " not less than " + Str(RH))
EndIf
CompilerElse
UnitTest::_handleAssert("AssertLess", Description, #False, #PB_Compiler_Procedure, "Cannot compare string")
CompilerEndIf
EndMacro
Macro AssertNotEmpty(String, Description)
CompilerIf TypeOf(String) <> #PB_String
UnitTest::_handleAssert("AssertNotEmpty", Description, #False, #PB_Compiler_Procedure, "Not a string")
CompilerElse
UnitTest::_handleAssert("AssertNotEmpty", Description, Bool(Len(String) > 0), #PB_Compiler_Procedure, "Length is zero")
CompilerEndIf
EndMacro
Macro AssertEmpty(String, Description)
CompilerIf TypeOf(String) <> #PB_String
UnitTest::_handleAssert("AssertEmpty", Description, #False, #PB_Compiler_Procedure, "Not a string")
CompilerElse
UnitTest::_handleAssert("AssertEmpty", Description, Bool(Len(String) = 0), #PB_Compiler_Procedure, "Length not zero")
CompilerEndIf
EndMacro
Macro Register(Method)
CompilerIf Defined(Method, #PB_Procedure)
UnitTest::_registerHandler(PreComp::_Str(Method), #PB_Compiler_FilePath + #PB_Compiler_Filename, @Method#())
CompilerElse
CompilerError "Test handler '" + PreComp::_Str(Method) + "' is not declared."
CompilerEndIf
EndMacro
EndDeclareModule
Module UnitTest
Structure sTest
name.s
file.s
*handler.pTestHandler
EndStructure
Structure sTests
List test.sTest()
testFail.w
testPass.w
groupFail.a
failed.a
EndStructure
Global gTests.sTests
Procedure _registerHandler(TestName.s, TestFile.s, *TestHandler.pTestHandler)
If TestName <> "" And *TestHandler <> #Null
AddElement(gTests\test())
With gTests\test()
\name = TestName
\file = TestFile
\handler = *TestHandler
EndWith
EndIf
EndProcedure
Procedure _handleAssert(AssertType.s, Description.s, Result, Test.s, FailReason.s = "")
ConsoleColor(7, 0)
Print(" - " + LSet("(" + AssertType + ")", 18) + " " + Test + " - " + Description + ": ")
If Result
ConsoleColor(10, 0)
PrintN("Passed")
Else
ConsoleColor(12, 0)
If FailReason = ""
PrintN("Failed")
Else
Print("Failed")
ConsoleColor(3, 0)
PrintN(" (" + FailReason + ")")
EndIf
gTests\testFail + 1
gTests\failed = #True
EndIf
EndProcedure
Procedure RunAll(PauseOnExit = #False)
Protected.s currentFile
OpenConsole("Unit Tests")
EnableGraphicalConsole(#True)
SortStructuredList(gTests\test(), #PB_Sort_Ascending | #PB_Sort_NoCase, OffsetOf(sTest\file), #PB_String)
ConsoleColor(7, 0)
PrintN("Running " + ListSize(gTests\test()) + " unit tests...")
gTests\groupFail = #False
ForEach gTests\test()
With gTests\test()
ConsoleColor(7, 0)
If currentFile <> \file
PrintN("")
PrintN("Running tests for: " + \file)
PrintN("")
currentFile = \file
EndIf
gTests\failed = #False
\handler()
ConsoleColor(7, 0)
PrintN("")
Print(\name + " tests: ")
If gTests\failed = #False
ConsoleColor(10, 0)
PrintN("Passed")
gTests\testPass + 1
Else
ConsoleColor(12, 0)
PrintN("Failed")
EndIf
PrintN("")
EndWith
Next
ConsoleColor(7, 0)
If gTests\testFail > 0
Print(Str(gTests\testFail) + " tests ")
ConsoleColor(12, 0)
PrintN("Failed")
Else
Print("All tests ")
ConsoleColor(10, 0)
PrintN("Passed")
EndIf
If PauseOnExit
ConsoleColor(7, 0)
PrintN("")
PrintN("Hit return to exit.")
Input()
EndIf
If gTests\testFail > 0
End - 1
EndIf
End 0
EndProcedure
EndModule
; ==============================================
; Sample Start
; ==============================================
CompilerIf #PB_Compiler_IsMainFile
Procedure UnitTest_BasicTests()
UnitTest::Assert(#True = #True, "Assert Test")
UnitTest::AssertTrue(#True = #True, "AssertTrue Test")
UnitTest::AssertFalse(#True = #False, "AssertFalse Test")
EndProcedure : UnitTest::Register(UnitTest_BasicTests)
Procedure UnitTest_ComparisonTests()
Protected value1.i, value2.i
value1 = 100
value2 = 200
UnitTest::AssertEqual(value1, value1, "AssertEqual Test")
UnitTest::AssertNotEqual(value1, value2, "AssertNotEqual Test")
UnitTest::AssertLess(value1, value2, "AssertLess Test")
UnitTest::AssertGreater(value2, value1, "AssertGreater Test")
EndProcedure : UnitTest::Register(UnitTest_ComparisonTests)
Procedure UnitTest_StringTests()
Protected string1.s, string2.s, string3.s
string1 = "Hello"
string2 = "World"
string3 = ""
UnitTest::AssertNotEqual(string1, string2, "AssertNotEqual String Test")
UnitTest::AssertNotEmpty(string1, "AssertNotEmpty String Test")
UnitTest::AssertEmpty(string3, "AssertEmpty String Test")
EndProcedure : UnitTest::Register(UnitTest_StringTests)
Procedure UnitTest_FailTests()
Protected value1.i, value2.i
Protected string1.s, string2.s, string3.s
value1 = 100
value2 = 200
string1 = ""
string2 = "Hello"
string3 = "Not Empty"
UnitTest::AssertEqual(value1, value2, "AssertEqual Test (Should Fail)")
UnitTest::AssertNotEqual(value1, value1, "AssertNotEqual Test (Should Fail)")
UnitTest::AssertLess(value2, value1, "AssertLess Test (Should Fail)")
UnitTest::AssertGreater(value1, value2, "AssertGreater Test (Should Fail)")
UnitTest::AssertNotEqual(string2, string2, "AssertNotEqual String Test (Should Fail)")
UnitTest::AssertNotEmpty(string1, "AssertNotEmpty String Test (Should Fail)")
UnitTest::AssertEmpty(string3, "AssertEmpty String Test (Should Fail)")
EndProcedure : UnitTest::Register(UnitTest_FailTests)
UnitTest::RunIfMain(#True)
CompilerEndIf