Super simple testing framework for AutoHotkey.
Yunit is designed to aid in the following tasks:
- Automated code testing.
- Automated benchmarking.
- Basic result reporting and collation.
- Test management.
See doc/Example.ahk for a working example script that demonstrates Yunit being used for testing.
A basic test setup looks like the following:
Yunit.Use(YunitStdout).Test(TestSuite)
class TestSuite
{
SomeTest()
{
return True
}
}
Installation is simply a matter of adding the Yunit folder to the library path of your project.
An example directory structure is shown below:
+ SomeProject
| + Lib
| | + Yunit
| | | LICENSE.txt
| | | README.md
| | | ...
| | + OtherLibrary
| | | ...
| README.md
| SomeProject.ahk
| ...
In AutoHotkey v1.1, library locations are checked as follows:
- Local library: %A_ScriptDir%\Lib\
- User library: %A_MyDocuments%\Lib\
- Standard library: %A_AhkPath%\Lib\
Yunit and its modules must be imported to be used:
#Include <Yunit\Yunit> ;import the basic test routines (mandatory)
#Include <Yunit\Window> ;import the window output module (optional)
#Include <Yunit\Stdout> ;import the stdout output module (optional)
#Include <Yunit\OutputDebug> ;import the OutputDebug output module (optional)
#Include <Yunit\JUnit> ;import the JUnit output module (optional)
Output modules only need to be imported if they are going to be used.
Yunit is implemented as a class, conveniently named Yunit
.
This class is static, which basically means you do not need to make a new instance of Yunit
it to use it.
To begin, first we need to select the output modules to use (a list of available modules is documented in the Modules section below). In other words, where the results of the tests should go.
This is done using the Yunit.Use(Modules*)
method, where Modules*
represents zero or more modules to use.
When called, the method returns a Yunit.Tester
object, which represents the options and settings for a group of tests:
Tester := Yunit.Use(YunitStdout, YunitWindow)
This code creates a Yunit.Tester
object that uses the YunitStdout
and YunitWindow
modules for output.
Now that the Yunit.Tester
object has been created, we can run a set of tests against it.
This is done using the Yunit.Tester.Test(Classes*)
method, where Classes*
represents zero or more test classes to use (the format is documented in the Tests section below).
When called, the method starts the tests and manages the results:
Tester.Test(FirstTestSet, SecondTestSet, ThirdTestSet)
This code runs all tests in all three sets.
This method is synchronous and blocks the current thread until complete. Results will be shown while the method is running.
Instead of using the two lines above, it may be preferable to simply chain them together:
Yunit.Use(YunitStdout, YunitWindow).Test(FirstTestSet, SecondTestSet, ThirdTestSet)
Multiple output modules are available:
Tester := Yunit.Use(YunitStdout)
This module writes the test results to the standard output. To see the output, use this at the command prompt:
"%ProgramFiles%\AutoHotkey\AutoHotkey.exe" "TestsScript.ahk" | more
The results are formatted one per line, each entry being in the following form:
Result: Category.TestName Data
- Result - result of the test ("PASS" or "FAIL").
- Category - category or categories that the test is located under, with subcategories separated by dots (Category.Subcategory.OtherCategory).
- TestName - name of the test being run.
- Data - data given by the test, such as specific error messages or benchmark numbers.
Tester := Yunit.Use(YunitOutputDebug)
This module writes the test results via OutputDebug. To see the output, see how to use OutputDebug
The results are formatted one per line, each entry being in the following form:
(Counter) Result: Category.TestName Data
- Counter - Number of the performed test
- Result - result of the test ("PASS" or "FAIL").
- Category - category or categories that the test is located under, with subcategories separated by dots (Category.Subcategory.OtherCategory).
- TestName - name of the test being run.
- Data - data given by the test, such as specific error messages or benchmark numbers.
Tester := Yunit.Use(YunitJUnit)
This module writes the test results JUnit compatible XML-Output. The XML-File is generated in the directory where the Test-script resides and has the name junit.xml.
The JUnit format is commonly used on Continuous-Integration servers (like jenkins)
See here for more information on the JUnit-format
Tester := Yunit.Use(YunitWindow)
This module displays the test results in a window with icons showing the status of each test.
The results are shown in the form of a tree control, with each test suite having a top level node, and categories or tests having child nodes.
Beside each node is an icon:
- Green up arrow - test passed successfully.
- Yellow triangle with exclamation mark - test failed.
- Two papers - test result/description.
Tests that result in data will have an additional child node that can be expanded to show it.
Test suites are written as classes. Class methods are considered tests; nested classes are considered categories. Classes nested within these nested classes are considered subcategories, and so on:
class TestSuite
{
This_Is_A_Test()
{
;...
}
class This_Is_A_Category
{
This_Is_A_Test()
{
;...
}
This_Is_Another_Test()
{
;...
}
}
}
The above corresponds to the following test structure:
TestSuite:
This_Is_A_Test
This_Is_A_Category:
This_Is_A_Test
This_Is_Another_Test
The test and category names are determined from their identifiers in the code. Test and category names may be duplicated as long as they are in different categories.
The order in which tests are called is arbitrary.
A test is a class method that takes no arguments and has no return value.
For a test to fail it must throw an exception.
Any test that returns normally is considered a success.
The method Yunit.Assert(Value, Message)
conveniently throws an exception when Value
evaluates to false, with an optional Message
which is displayed if it fails. If the Assert() fails however, no subsequent code within the test function will execute. So, it should always be the last line within the function.
This_Is_A_Test()
{
Yunit.Assert(1 = 1) ;test passes
}
This_Is_Another_Test()
{
Yunit.Assert(1 = 2, "Description of the failure") ;test fails
MsgBox, hello world ;this never runs since the test above failed
}
The special test methods Begin()
and End()
, if present, will be called on the instance of the class before and after each test, respectively.
Use this to do setup on the this
object for each test in a category.
In addition, the special class methods __New()
and __Delete()
are called before testing starts on a category and after it finishes, respectively.