📜 ⬆️ ⬇️

Unit testing for applications on the IBM Notes / Domino platform

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:


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:
')

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:

  1. tests will be written in the form of class methods;
  2. tests will run the agent;
  3. when writing tests, you will need a set of checks (asserts, assert);
  4. 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:


When implementing the question arose: how to fix the statistics? Again, there is no means of reflection and annotations, which means:


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) '     Call Me.EndTest(funcName) GoTo endh ErrorHandler: Error Err, "(" & DESIGN & ") " & FuncName & ", line " & Erl & Chr(10) & Error$ endh: End Sub 

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) '     On Error ERROR_CODE_TO_TEST GoTo AssertErrorHandler ',    ,     GoTo endh AssertErrorHandler: Call Me.EndTest(funcName) Resume endh ErrorHandler: Error Err, "(" & DESIGN & ") " & FuncName & ", line " & Erl & Chr(10) & Error$ endh: End Sub 

From these reflections, a test context was born that stores test runtime information:


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 ()" 'Override GoTo endh ErrorHandler: Call Me.TestFailed(Error$) Resume Next endh: End Sub End Class 

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:


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:

  1. simplify the writing of tests so that you can get rid of a number of conventions when they are created;
  2. get rid of the need to manually create test classes and call test methods;
  3. 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!

Source: https://habr.com/ru/post/346708/


All Articles