I will touch on a topic that is virtually unknown in the world of Notes / Domino - unit testing, and I’ll bring to your attention a tool for writing unit tests for Notes / Domino under the LotusScript language. The article is mainly designed for those who work with the IBM Notes / Domino platform (Lotus Notes) and is developing using the LotusScript language.
Some terminology to be used in the article:
- An application is an IBM Notes / Domino database or collection of databases if they are a single logical unit.
- The client is a fat client, IBM (Lotus) Notes software.
- Server - IBM Domino server software.
I'll start by describing how the development looks, so that everyone can look at themselves. This, in the first place, is about writing applications for a fat client using @-formulas and the LotusScript language. Both languages are scripted, and you can write code almost everywhere: in events and actions of UI objects (such as forms and views), in libraries (only for LotusScript language), agents, and other elements of the base design. In addition, for each event and action, you can independently select the language in which to write. In the end, if you do not have some approach to the organization of the code, it turns out a terrible noodle-like porridge from the code scattered throughout all imaginable and inconceivable places. And now we add to this a large zoo of applications, and, usually, a small staff of specialists.
I was vaccinated (for which I am very grateful) a certain approach to writing code, which I later developed. The result was a whole system that talks not only about where to write, but how to write. If in brief:
')
- all business logic is written in LotusScript, using @ formulas is minimal;
- all code is in libraries, and is divided into:
- code that can be used both on the server and on the client;
- code that can be used exclusively on the client;
- in visual elements (forms, representations) only calls to the library code are used, and, in the overwhelming majority, single-line ones;
- maximum use of object and minimum use of procedural features of the LotusScript language.
So what's the deal with unit testing? The more complex, multi-link system is obtained, the more difficult it is to keep all its components in mind. Inevitably, edits in one place lead to breakdowns in another. Often, a large number of options for what is happening, the number of parameters and dependencies with limited resources, make it almost impossible to manually test all variations. You can not dismiss the fear of change, because it is not always possible to remember, and even more to know the features of the code that underlies. The presence of tests can both delineate the boundaries of the interactions, as well as guarantee the performance check. Yes, this is an additional effort to cover the tests, their preparation, but, as it seems to me, they are worth it.
A google companion who assisted in finding the means of unit testing for IBM Notes / Domino under the LotusScript language resulted in only one very old version of the project on
OpenNTF.org . In general, the idea that was spinning in my head was similar, but the implementation promised a few more. Therefore, it was decided to write my own, not afraid of this word, the framework for unit testing
with blackjack and courtesans is quite flexible and extensible.
For the basis of building the test logic, I took the usual for any (probably) framework for unit-testing: a class with test methods. These tests should be collected in a bunch and chased away. So, in my head made some list of what is needed for the minimum set:
- tests will be written in the form of class methods;
- tests will run the agent;
- when writing tests, you will need a set of checks (asserts, assert);
- as part of the test run, test success statistics should be maintained and the string displayed in the test where the error occurred.
Some aspects and assumptions that were made in the design of implementation:
- LotusScript has neither reflection nor annotations in its arsenal, which means that it will be necessary to explicitly indicate which methods are test methods. Variants, for example, with parsing the library with test classes were swept away, at least for the first stage.
- Minimum set of checks:
- comparison of expression with truth;
- a lie;
- equality comparison. Since the question of equality is a context-sensitive thing, an object will be passed to the equality comparison method. The class of this object must be inherited from the interface class (there are no pure interfaces in LotusScript) with one public method that returns a logical value;
- error checking, namely, the error code.
When implementing the question arose: how to fix the statistics? Again, there is no means of reflection and annotations, which means:
- or wrap each test method in the agent that runs them with a code with statistics;
- or enter it into the test method itself.
Decided to go the second way. As a result, the AbstractTest class was born with two public methods: BeginTest (TEST_NAME), EndTest (TEST_NAME). Why public? This was done for convenience, due to the features of the built-in IDE based on Eclipse: private methods of the superclass are visible to descendants and are available for redefinition, but they are not included in the hints of the IDE itself. The pattern of each test method for a successful script looks like this:
Public Sub TestMethod() On Error GoTo ErrorHandler Const FuncName = "TestClassName.TestSubName ()" Call Me.BeginTest(funcName)
For test methods with the expected error, it is slightly different and looks like this:
Public Sub TestError() On Error GoTo ErrorHandler Const FuncName = "DemoTest.TestAssertErrorExample ()" Call Me.BeginTest(funcName)
From these reflections, a test context was born that stores test runtime information:
- data on the status of passing tests;
- the ability to add tests and status changes;
- logging
The class responsible for running the tests has undergone standardization. If you minimize the code, it consists of the main Run () method and the method that should be overridden in the agent that runs the DoRunTests () tests. The DoRunTests method should just contain the creation of test classes and a call to the corresponding test methods:
Class AbstractTestRunner Public Function Run() On Error GoTo ErrorHandler Const FuncName = "AbstractTestRunner.Run ()" [...] Call Me.DoRunTests() [...] GoTo endh ErrorHandler: Error Err, "(" & DESIGN & ") " & FuncName & ", line " & Erl & Chr(10) & Error$ endh: End Function Private Sub DoRunTests() On Error GoTo ErrorHandler Const FuncName = "AbstractTestRunner.RunTests ()"
Everything is simple with checks. Only a comparison method for equality can deserve a small separate attention:
Public Sub Equals(matcher As IMatcher) On Error GoTo ErrorHandler Const FuncName = "Assert.Equals ()" If Not Matcher.Matches() Then Call Me.Throwerror(funcName) GoTo endh ErrorHandler: Error Err, "(" & DESIGN & ") " & FuncName & ", line " & Erl & Chr(10) & Error$ endh: End Sub
As mentioned above, it accepts an object whose class is inherited from the interface class (IMatcher), which has only one public method Matches () as boolean.
Class IMatcher Public Function Matches() As Boolean End Function End Class
It also implemented several simple matchers that were moved to a separate library:
- SimpleValueMatcher is a simple comparison of 2 values. Values are accepted with type Variant.
- DocumentItemsMatcher - a comparison of the specified fields in two specified documents. The comparison is made for the presence of a field, for type and value.
- DatabaseMatcher - a comparison of two databases: replica id, path and name. Essence - we deal with the same base. It is advisable here to add another comparison to the server.
- DocumentMatcher - comparison of 2 documents for identity: they are in the same database, have the same UniversalId, match in all fields.
The project is publicly available on
GitHub , distributed under the Apache 2.0 license and provided with detailed installation and use documentation. Also, the project was placed on the most popular site for domino-
vodov -
Openntf.org .
As a development I see:
- simplify the writing of tests so that you can get rid of a number of conventions when they are created;
- get rid of the need to manually create test classes and call test methods;
- create a centralized application to run and collect test results.
It so happened that the system, which showed all the need to create a unit-test for Domino and prompted me to write the presented tool, is currently not covered by tests. However, I am preparing another project that should initially become public. The first version is ready and 100% covered by tests created on the framework described. I am going to apply statistics of passing tests to each release.
Constructive ideas, suggestions and comments are always welcome!