Gambas 单元测试
gb.test 是用于单元测试和测试驱动开发(TDD)的Gambas组件。有了这个组件,你可以为Gambas程序创建测试,你可以在测试驱动的情况下开发软件(先写测试,然后写程序功能),你可以确保在重构或增强软件时,代码的预期结果保持不变。因此,gb.test有助于确保Gambas程序具有更好的稳定性和更高的可靠性。
You can write the tests in special test classes (class files with the ending '.test').
gb.test runs the tests and the results are output as a
TAP string together with a summary. You can start the testing inside the Gambas IDE and watch the progress of the testing as well as the result in the Unit Tests tab. The summary at the end shows "PASSED" or "FAILED" so you can easily see if your tests have been successful or not. But they also can be displayed, analyzed or viewed with any
Tap consumer.
You can also run the tests of your project on the command line and so gb.test provides the tools to run tests automatically.
How gb.test works
gb.test is there to test a project from the inside. It can access all procedures and variables that are public within a project.
By default, gb.test executes all tests that exist in a project. The tests are grouped into test modules. test modules are classes with the extension '.test'. They contain public methods (we call them 'test methods'). The names of the test methods must not begin with '_'.
gb.test organizes the order in which the test methods are called in test modules in alphabetical order. This results in this sequence of execution:
TestModuleA.TestMethodA
TestModuleA.TestMethodB
...
TestModuleB.TestMethodA
TestModuleB.TestMethodB
The test methods themselves contain the actual tests, which are performed with methods of the class 'Assert'. The class 'Assert' provides various assertions, which are checked in the gb.test process.
For example, the assertion 'Assert.Equals(Got as String, Expected as String)' can be used to test whether the content of the variable 'Got' is identical to the content of the variable 'Expected'.
If yes, gb.test outputs 'ok', if no, gb.test outputs 'not ok' to Stdout.
So basically gb.test works through all assertions, in all test methods in all test modules of a project, checks the assertions and outputs the result to stdout. In addition, gb.test also runs a statistic and at the end it outputs a summary that shows if and which errors were found.
If you don't want gb.test to perform all tests it finds in a project, you can limit its work with so-called test suites to single or groups of tests. A test-suite is basically nothing more than a string containing the names of the desired tests that gb.test should execute.
Example
There is an example in
this simple Gambas project.
Test module
In test driven development it is common practice to first write the test that the future program will later fulfill. Therefore we start by creating a test module.
This is a class, named like any other Gambas class with the ending ".test", for example "THelloWorld.test". This class contains one or more tests, these are public subs whose name must not contain an underscore:
' Gambas test module file
''' Test module THelloWorld
Public Sub AHelloWorld()
Test.Note("I am the first test, because my name starts with A.")
Assert.Equals(Hello.World(), "Hello World", "HW strings should be equal")
End
With the methods provided by the Assert class you are able to test the output of any public state (variable, constant or function) in your project.
Module (Function) to test:
Now that we have written the test, it is time to develop the program that will fulfill the test. So we create a function "World" in a module "Hello" in our project:
' Gambas module file
''' Module is named "Hello"
'' Returns "World"
Public Function World() As String
Return "Hello World"
End
Running the test
If you are working with the Gambas IDE you can hit F4 to let your tests run. The IDE will show you the "Unit tests" tab with this content:
Test THello:AHelloWorld
# I am the first test, because my name starts with A.
ok 1 - HW strings should be equal
1..1
ok 1 - THello:AHelloWorld
# Ran: 'THello.AHelloWorld'
#
# PASSED
If a failure occurs it will report FAILED instead of PASSED and will show you the place of the failure. If you want to debug the code you can set a breakpoint anywhere in your test or production code, hit <F4> again and start debugging.
Test modules and test methods can be named the same way as any Gambas module or method except that a test method may not be named _Setup(), _Teardown(), _SetupEach() or _TeardownEach() and it's name must not contain an underscore.
Testsuites
If you hit Shift-F4 the Gambas IDE lets you create so called "test suites". This is convenient if you have a lot of tests but need to run just one or a couple of them by chance. You can create a lot of test suites and use them to test different aspects of your project separately.
Testsuites are stored in the file .test in the project's path.
Test your project on the console
You also can test your project on the console. It has to be compiled first. The command
gbx3 -T "*" /path/to/my/project executes the unittests and prints the result to standard output. You can call single tests or an arrangement of tests in the same way, as these are stored in the file .test after you created a test suite.
You can even let run a testsuite by name by adding a preceeding "@" to it's name if you let it run with gbx3:
gbx3 -T "@my first testsuite" /path/to/my/project
If your project relies on the existence of environment variables you can define these on the command line:
MyEnvVar=1 gbx3 -T "@my first testsuite" /path/to/my/project
Environment variables defined inside the
Project properties
dialog of the Gambas IDE are not taken into account.
Test fixture
Sometimes it is neccessary to create a "fixture", a special environment for a test or a couple of tests, and to destroy this environment after the test is done. For example a database connection should be established at the beginning of all tests, some tables for testing should be created and deleted for every single test method and the database connection should be closed at the end. This can be done with _Setup... and _Teardown... functions inside the test module.
Sub _Setup() and Sub _Teardown()
You can create methods with these names to create an environment for all test methods inside a test module, in the beginning _Setup() is invoked and after all test methods inside the test module are done you can destroy the environment with _Teardown().
Sub _SetupEach() and Sub _TeardownEach()
You can create methods with these names to create an environment for each test method before it is invoked and to destroy it afterwards. If you have five test methods inside your test module these functions will be invoked five times, _SetupEach() before each test method, _TeardownEach() after each test method. Got it?
Plan tests
It would be pretty stupid if your test system, for unknown reasons, swallowed single tests and never showed you that they did not run. To protect yourself from this, there is a way to specify a plan that defines the number of assertions that must run inside a testmethod. And it goes like this:
' Gambas test file
''' Test module THelloWorld
Public Sub AHelloWorld()
'' Defines the count of assertions in this test method
Test.Plan(1)
Assert.Equals(Hello.World(), "Hello World", "HW strings should be equal")
End
If the tests have not fulfilled the plan, the system will display "FAILED" and tell you the reason.