vba-tools / vba-test Goto Github PK
View Code? Open in Web Editor NEWAdd testing and TDD to VBA on Windows and Mac
License: MIT License
Add testing and TDD to VBA on Windows and Mac
License: MIT License
It would be great if AfterEach
functionality existed to be symmetrical with BeforeEach
.
Is this on the roadmap?
I really enjoy vba-tdd
for it's simplicity, however, I find it limiting when developing large test suites. The fact that everything must be in a single function can either make for very long messy suites or create a ton of small suites. Splitting into sub-suites is also difficult due to VBA's module name size limitation. I've ran into the Procedure too large
error several times so far too.
It would be helpful to support running test suite classes where each method is its own test; this will help with a number of things as I'll discuss below.
As an example, the AddTests
example could be written as:
Option Explicit
Public Sub ShouldAddTwoNumbers(ByRef Test As TestCase)
On Error GoTo UnexpectedError
Test.IsEqual Add(2, 2), 4
Test.IsEqual Add(3, -1), 2
Test.IsEqual Add(-1, -2), -3
On Error GoTo 0
Exit Sub
UnexpectedError:
Test.FailFromError Err
End Sub
Public Sub ShouldAddAnyNumberOfNumbers(ByRef Test As TestCase)
On Error GoTo UnexpectedError
Test.IsEqual Add(1, 2, 3), 6
Test.IsEqual Add(1, 2, 3, 4), 10
On Error GoTo 0
Exit Sub
UnexpectedError:
Test.FailFromError Err
End Sub
Note: Technically error handling within the test subroutine would be optional; see below.
Because VBA does not support programmatically creating classes the user would have to specify a "factory method" to the reporter:
Public Function CreateTestCaseSuite() As Tests_TestCase
Set CreateTestCaseSuite = New Tests_TestCase
End Function
Public Function CreateTestSuiteSuite() As Tests_TestSuite
Set CreateTestSuiteSuite = New Tests_TestSuite
End Function
Public Sub RunTests()
Dim Reporter As New WorkbookReporter
Reporter.ConnectTo TestRunner
Reporter.AddSuiteForFactory TestSuite.Create("TestCase"), "CreateTestCaseSuite"
Reporter.AddSuiteForFactory TestSuite.Create("TestSuite"), "CreateTestSuiteSuite"
Reporter.Done
End Sub
The reporter would be able to:
ShouldAddTwoNumbers
and should_add_two_numbers
would both convert to "should add two numbers"TestCase
for the TestSuite
based on the above name.TestCase
instance.Note: To support class reuse SetUp
/TearDown
and SetUpSuite
/TearDownSuite
subroutines could be used.
This has several benefits:
NumSuites
.In order to make TestSuite.Create
work the PredeclaredId
would have to be set to True.
It may also be helpful to add a generic runner class instead of using the WorkbookReporter/ImmediateReporter directly:
Public Sub RunTests()
Dim Runner As New TestSuiteRunner
Runner.AddReporter WorkbookReporter.Create(ThisWorkbook.Worksheets("TestRunner"))
Runner.AddReporter ImmediateReporter.Create()
Runner.AddSuiteForFactory TestSuite.Create("TestCase"), "CreateTestCaseSuite"
Runner.AddSuiteForFactory TestSuite.Create("TestSuite"), "CreateTestSuiteSuite"
Runner.Run
End Sub
Final closing notes: I bring this suggestion up because I really like the all-included aspect of vba-test
. While there are more powerful solutions available for vba testing (like RubberDuck/SimplyVBUnit (with modification)), they require external installation which in my particular application is not a feasible option. I strongly feel such an enhancement to vba-test
will make it much more flexible.
Thoughts? ๐
It would be very helpful if SpecDefinition
could return a reference to itself. For example:
With Specs.It("should be a good example")
Set Foo = Bar.FooBarIt()
VerifyIsAnObject(.Self, Foo)
End With
Private Function VerifyIsAnObject(ByRef SpecsIt As SpecDefinition, ByRef Foo As Bar)
With SpecsIt
.Expect(Foo...).ToBe...
End With
End Function
While I understand RunMatcher exists, you may not want to make your various functions public. RunMatcher also doesn't support the fluent Expect().ToBe() syntax used throughout the rest of our suites.
Not sure if I am missing something here or if I uncovered a memory leak. I was adding a Terminate
event to TestSuite
and noticed it doesn't fire.
Private Sub Class_Terminate()
Debug.Print "This should fire from TestSuite..."
Stop
End Sub
I was trying to find out why this was being skipped over and I came across this rubberduck blog lazy-object-weak-reference.
By storing the TestCase
objects in the Tests
collection it seems like it stays in memory (possibly even all the TestCase
objects as well then?). I don't know how to check if this actually is the case or if something else is going on preventing the event to fire.
I ran a test in which I set the collection to nothing and sure enough the Terminate
event fires. Perhaps TestSuite
should have it's own state of the various statuses of the tests vs holding references to all the TestCase
classes?
These would be useful helper asserts.
Tim, dumb question, but I don't know for sure. I see a lot of documentation generators online that support VB/VBA (more or less), and I don't see a note in the repo or Wiki that tells me.
Context: I am writing a new reporter to support a multi-file test runner, and I would like to make sure I am consistent with what your documentation generator expects.
Thanks!
This is partially related to #24 perhaps, but it would be useful to support failing a suite if an unhandled error occurred. This may be difficult to accomplish automatically in VBA, so it may require some wrapping in the "RunTests" entry point.
This is mostly to help assist in running the full suite uninterrupted (e.g. CI/CD environments).
Jasmine provides a 'toBeCloseTo' matcher for precision math comparison. It would be good for Excel-TDD to implement the same in SpecExpectation.cls
.
This new feature would assist in the unit testing of probabilistic data structures and algorithms, such as Bloom filters.
it("The 'toBeCloseTo' matcher is for precision math comparison", function() {
var pi = 3.1415926,
e = 2.78;
expect(pi).not.toBeCloseTo(e, 2);
expect(pi).toBeCloseTo(e, 0);
});
getJasmineRequireObj().toBeCloseTo = function() {
function toBeCloseTo() {
return {
compare: function(actual, expected, precision) {
if (precision !== 0) {
precision = precision || 2;
}
return {
pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2)
};
}
};
}
return toBeCloseTo;
};
Note: There has been some discussion in the Jasmine community of the most appropriate API to provide, either by way of an inverse exponent or a simple tolerance level. Worth discussing.
when assigning WorkbookReporter.ConnectTo()
, it will attach to any worksheet.
if you try to run WorkbookReporter.Start()
subsequently, a runtime error will occur if the sheet connected to does not contain specificly named ranges and shapes.
it would be great if Start()
or Initialize()
could do the following
me.pSheet
is null => create a new sheet with pretty report formatting and the required shapes and rangesme.pSheet
does not contain each of the required named ranges or shapes => create these entities, or describe to the user why it can't continue, and how to fix it.IsEqual should support Array Types so that users can compare if two arrays are identical (same size, index 0 matches index 0, etc.). Currently it falls through to the else clause:
IsEqual = Actual = Expected
and causes a VB runtime error.
VBA issue
It would be useful to be able to assert the Err
object and ensure that either no error occurred as expected, or that the expected error was called:
With Suite.Test("calling AcceptsLongsOnly with a long should not raise an error")
On Error Resume Next
Err.Clear
MyModule.AcceptsLongsOnly 123
.ToNotBeError Err
On Error GoTo 0
End With
With Suite.Test("calling AcceptsLongsOnly with non-longs should raise an error")
On Error Resume Next
Err.Clear
MyModule.AcceptsLongsOnly "ABC"
.ToBeError Err, vbObjectError + 10001, "Expected value to be Long."
On Error GoTo 0
On Error Resume Next
Err.Clear
MyModule.AcceptsLongsOnly True
.ToBeError Err, vbObjectError + 10001, "Expected value to be Long."
On Error GoTo 0
End With
Of course, the naming of ToNotBeError
and ToBeError
are only examples. It may be possible to build them into IsOk
and NotOk
, but I'd prefer a dedicated method with Err
or Error
in the name for clarity.
Tasks:
AfterEach
to teardown workbook after each testBeforeEach
and AfterEach
methods to allow passing in workbook helper instanceWBProxy
and Scenario
)WBProxy
in other IWBProxy
implementations. (lots of overlap currently)BeforeEach
and AfterEach
goal:
Dim Proxy As New WBProxy
' Initial setup/mapping...
Suite.BeforeEach "Setup", Instance:=Proxy
Suite.AfterEach "Teardown", Instance:=Proxy
Causes an issue with current arguments implementation, since ParamArray
cannot be used with other Optional
variables (Instance
), but ByRef
with BeforeEach
and AfterEach
is a goal, so ParamArray
might not be useful anyways.
Is this on the roadmap? If not do you know a way to manually mock out a function to check if it returns or to force a return value, etc?
Goal: Add performance tests to output of tests (wouldn't necessarily be pass/fail, just info).
Possible designs:
' Custom matcher
With Specs.It("speed test as custom matcher")
.Expect("Callback").RunMatcher "SpeedTest"
' Pros: Callback can be called multiple times (for average speed)
' Uses currently available methods
' Cons: Not the most intuitive API
End With
' Method that takes SpecDefinition and Callback
SpeedTest(Specs.It("speed test with definition and callback"), "Callback", Args...)
' Pros: Simple API, callback can be called multiple times
' Cons: More intuitive, but still not perfect
' Most likely design
' ------------------
' Run, Start, and Finish
SpeedTest.Run Specs, "speed test with Run", "Callback", Args...
SpeedTest.Start Specs, "speed test with Start and Finish"
' ...
SpeedTest.Finish
example test:
Tests.test("range should contain value").Includes myRange.value2, "myValue"
an error occurs because myRange.Value2
is a 2 dimensional array.
in ArrayIncludes()
, called by Includes()
VBA.IsArray(Value(i))
causes an error if Value() is multidimensional
There are at least two code examples in this project which are functions but end with "End Sub":
This is a trivial issue, I know. It's a documentation issue really rather than a source code issue. But since the fix is also trivial I figured I might as well raise it.
Fix: Change "End Sub" to "End Function"
P.s. Thanks for the amazing test harness!
Its is nice to have the examples that are there, but I think there needs to be some written examples showing how to put it all together (as upposed to loading "VBB-TDD Specs.xlsm" and reverse engineering).
Class events in VBA is kind of a pain
The only way it seems to execute the before, after, and Result events of the TestSuite seems to be through the TestFixture() described in the README, and used in many other VBA-Tools implementations.
Why isn't TestFixture() distributed in ./src/?
Tasks:
SpecSuite
SpecSuite
as SpecSuite.(Print, Results, Debug, etc.)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.