Step 2 - The problem with String Concatenation
Now that we have the empty Test Project, we can add some code to illustrate the string problem. Let's do this in a new class, so that I can show you how to add a new Fixture:
Adding a new Test Fixture
Click on "Project / Add Class Module" and select "vbUnit TestFixture":
Figure 5: New Fixture, in Project / Add Class Module
Rename the Fixture class into "Step2" and add it to the TestSuite by adding the following line to the ISuite_Suite method:
'vbUnitTutorialSuite.cls Option Explicit Implements ISuite Private Function ISuite_Suite() As ITest Dim suite As New TestSuite suite.SuiteName = "vbUnit Tutorial Suite" 'TODO: Add your Fixtures here suite.AddFixture New Step1 suite.AddFixture New Step2 Set ISuite_Suite = suite End Function
Run the project again in the vbUnit window. You should now get two failed assertions, one from the Step1 fixture, and one from Step2:
Figure 6: second run, now with two default fixtures
A little performance measurement
Now let's actually do something useful with the fixture class and add some code. The problem to be examined is String concatenation. Let's examine the problem with a little performance analysis. But before that, comment out the line suite.AddFixture New Step1 in your TestSuite, since we won't need the Step1 fixture for now. After that, your TestSuite should look like this:
'vbUnitTutorialSuite.Suite Private Function ISuite_Suite() As ITest Dim suite As New TestSuite suite.SuiteName = "vbUnit Tutorial Suite" ' suite.AddFixture New Step1 'disable this fixture for now suite.AddFixture New Step2 Set ISuite_Suite = suite End Function
Now, replace the code in the Step2 fixture with the following:
'Step2.cls Option Explicit Implements IFixture Private m_assert As IAssert Private m_timer As PerformanceCounter Private Sub IFixture_Setup(assert As IAssert) Set m_assert = assert Set m_timer = New PerformanceCounter m_assert.EnablePrintMsg End Sub Private Sub IFixture_TearDown() End Sub Public Sub TestConcat1000Strings() Dim str As String Dim l As Long m_timer.Start For l = 0 To 1000 str = str & "This is the " & l & "nd line in the string." _ & vbCrLf Next l m_timer.Finish m_assert.PrintMsg m_timer.ToString, "msg1" End Sub Public Sub TestConcat2000Strings() Dim str As String Dim l As Long m_timer.Start For l = 0 To 2000 str = str & "This is the " & l & "nd line in the string." _ & vbCrLf Next l m_timer.Finish m_assert.PrintMsg m_timer.ToString, "msg1" End Sub
This code is running two TestMethods, TestConcat1000Strings and TestConcat2000Strings. Both methods use a timer to measure the amount of time it takes to concatenate a number of strings. The resulting time is sent to the Results window with m_assert.PrintMsg. Printing messages is purely for the benefit of the human programmer, and they have no effect on the success or failure of the test. That's ok because at this point we are using vbUnit to experiment with some pieces of code instead of writing Unit Tests.
Save your project and run it in the TestRunner. On a relatively old machine, I get the following result:
MESSAGES: 2
Step2.TestConcat1000Strings: 640 ms
Step2.TestConcat2000Strings: 3112 ms
In other words, concatenating two times as many strings takes about five times as long. If you are feeling adventurous or have a very powerful machine, try increasing the number of iterations to 10000 and 20000, and then watch the memory consumption of VB6.EXE in the Task Manager (Warning: SAVE EVERYTHING before you try this).
On my machine, I got:
1000 Strings: 640 ms = 0.01 Minutes
10000 Strings: 152,189 ms = 2.5 Minutes
20000 Strings: 632,876 ms = 10.5 Minutes
Concatenating 20000 strings takes about 1000 times longer than concatenating 1000 strings, even though the task size is only increased by a factor of 20. This algorithm belongs to the worst of its kind!
Although these examples are a little extreme, they illustrate a potential performance problem for the vbUnit application. Even with relatively moderate result sets, there is already a noticeable delay when the result is formatted.
The explanation for this poor performance is simple: When VB processes an expression of the form str = str & "some text", it calculates the new length of str, allocates a corresponding amount of memory, copies str + "some text" into the newly allocated memory, and then releases the memory for the previous copy of str. Hence, adding a single character to a string of 5000 characters will result in 5001 characters being copied. Adding a single character to a string of 5000 characters twice in two successive operations will result in 10003 characters being copied. In addition to the copying, there is the overhead of memory management. Incremental string concatenation in VB is really expensive!
In the next step we will look at ways to overcome this problem.
Summary
This step has shown how to use vbUnit to quickly run and compare some pieces of code without the need to write a user interface based on the usual "Form1".
- add a new Fixture class with "Project / Add Class Module" and select "vbUnit TestFixture"
- then register the name of the new fixture in the ISuite_Suite method of your TestSuite
- vbUnit is not just for unit testing, it can also be used for quick prototypes and experiments
- send messages to the Results window with m_assert.PrintMsg
- PrintMsg must be enabled with m_assert.EnablePrintMsg. This can be located in IFixture_Setup