вторник, 13 октября 2015 г.

#847. Let’s create tests for script words

Original in Russian: http://programmingmindstream.blogspot.ru/2015/09/blog-post.html
The previous series was here – Introduction. Let’s create tests for script words.

I separated “words in dictionary” from tests there.

Now I will put them together:

string.ms.dict:

// string.ms.dict.web
 
USES
 Documentation.ms.dict
 params.ms.dict
 core.ms.dict
 map.ms.dict
 Testing.ms.dict
 io.ms.dict
;
 
: (string)
 ^ IN aValue
 aValue DO ToPrintable
; // (string)
 
STRING FUNCTION string:CatWihAny
 STRING IN aString
 IN aValue
 aString aValue ToPrintable Cat =: Result
; // string:CatWihAny
 
STRING FUNCTION any:Cat
 ARRAY IN anArray
 anArray .map> ToPrintable strings:Cat =: Result
; // any:Cat
 
TestsFor any:Cat
 Test T1 [ 'A' 123 'B' ] any:Cat Print ;
 Test T2 [ 'A' 124 'B' ] any:Cat Print ;
; // TestsFor any:Cat
 
STRING FUNCTION (+)?
 STRING in aPrefix
 STRING right aSuffix
 %SUMMARY 'If aSuffix is not empty, it returns the sum of aPrefix and aSuffix, otherwise or returns a blank string' ;
 Result := ''
 STRING VAR l_Suffix
 aSuffix =: l_Suffix
 if ( l_Suffix =/= '' ) then
  ( aPrefix l_Suffix Cat =: Result )
; // (+)?
 
TestsFor (+)?
 Test T1 '' (+)? 'B' Print ;
 Test T2 'A' (+)? 'B' Print ;
 Test T3 'A' (+)? '' Print ;
 Test T4 'A' (+)? 'D' Print ;
 Test T5 'A' (+)? '123' Print ;
; // TestsFor (+)?
 
STRING FUNCTION ?(+)
 STRING in aPrefix
 STRING right aSuffix
 %SUMMARY 'If aPrefix is not empty, it returns the sum of aPrefix and aSuffix, otherwise or returns a blank string ' ;
 Result := ''
 if ( aPrefix =/= '' ) then
  ( aPrefix aSuffix Cat =: Result )
; // ?(+)
 
TestsFor ?(+)
 Test T1 '' ?(+) 'B' Print ;
 Test T2 'A' ?(+) 'B' Print ;
 Test T3 'A' ?(+) '' Print ;
; // TestsFor ?(+)
 
STRING FUNCTION strings:CatSep>
 STRING right aSep
 ARRAY right aValues
 aValues aSep strings:CatSep =: Result
; // strings:CatSep>
 
TestsFor strings:CatSep>
 Test T1 strings:CatSep> ' ' [ 'A' 'B' ] Print ;
 Test T2 strings:CatSep> ' ' [ 'A ' 'B' ] Print ;
 Test T3 strings:CatSep> ' ' [ 'A ' ' B' ] Print ;
 Test T4 strings:CatSep> ' ' [ 'A' ' B' ] Print ;
 Test T5 strings:CatSep> ' ' [ '' 'B' ] Print ;
 Test T6 strings:CatSep> ' ' [ 'A' '' ] Print ;
; // TestsFor strings:CatSep>
  
WordAlias CatSep> strings:CatSep>

String.ms.script:

USES
 Documentation.ms.dict
 string.ms.dict
 Testing.ms.dict
;
 
RunTests (+)?
 %REMARK 'Launch the “standard tests” for the word (+)?'
RunTests ?(+)
 %REMARK 'Launch the “standard tests” for the word ? (+)'
RunTests strings:CatSep>
 %REMARK 'Launch the “standard tests” for the word strings:CatSep>'
RunTests any:Cat
 %REMARK ' Launch the “standard tests” for the word any:Cat'

The result of the tests:

String.ms.script.out

Testing: (+)?
T1
B
T2
AB
T3
 
T4
AD
T5
A123
Testing end: (+)?
------------------
Testing: ?(+)
T1
 
T2
AB
T3
A
Testing end: ?(+)
------------------
Testing: strings:CatSep>
T1
A B
T2
A B
T3
A  B
T4
A  B
T5
B
T6
A
Testing end: strings:CatSep>
------------------
Testing: any:Cat
T1
A123B
T2
A124B
Testing end: any:Cat
------------------

In order to put it together I introduced the word TestsFor and RunTests.

TestsFor – determines tests for the word.
RunTests – launches tests for the word.

These look as follows:

Testing.ms.dict:

// Testing.ms.dict
 
USES
 axiom_push.ms.dict
 macro.ms.dict
 params.ms.dict
 io.ms.dict
 EngineTypes.ms.dict
 Documentation.ms.dict
;
 
CONST cTests 'Tests:'
 
MACRO TestsFor
 ENGINE_WORD RIGHT LINK IN aName
  %REMARK 'aName link to the word at the right of TestsFor'
 %SUMMARY 'Determines the tests set for word aName' ;
 
 axiom:PushSymbol VOID
 axiom:PushSymbol axiom:operator
 cTests aName |N Cat Ctx:Parser:PushSymbol
; // TestsFor
 
PRIVATE PROCEDURE DoRunTestsFor
 STRING IN aTestedWordName
 ENGINE_WORD IN aTestsHolder
 %SUMMARY 'Executes tests for aTestsHolder' ;
 
 [ 'Testing: ' aTestedWordName ] strings:Cat Print
 aTestsHolder MembersIterator ==> (
  IN aTest
   %REMARK 'aTest – embedded element aTestsHolder'
  if ( ( aTest %ST |N ) = ( NameOf Test ) ) then
   %REMARK '- filtrates tests only.'
  begin
   aTest |N Print
    %REMARK 'Print the test name'
   aTest DO
    %REMARK 'Launch the test'
  end // ( ( aTest %ST |N ) = 'Test' )
 )
 [ 'Testing end: ' aTestedWordName ] strings:Cat Print
 '------------------' Print
; // DoRunTestsFor
 
MACRO RunTests
 ENGINE_WORD RIGHT LINK IN aName
  %REMARK 'aName link to the word at the right of RunTests'
 %SUMMARY 'Executes tests for aName' ;
 
 STRING VAR l_Name
 aName |N >>> l_Name
 STRING VAR l_TestsHolderName
 cTests l_Name Cat >>> l_TestsHolderName
 
 l_Name Ctx:Parser:PushString
 axiom:PushSymbol @
 l_TestsHolderName Ctx:Parser:PushSymbol
 axiom:PushSymbol DoRunTestsFor
; // RunTests

Nothing “exorbitant”.

The test just “moved” closer to the code for testing.

Yet, this is actually not so bad.

It is sort of “encapsulation of code and its contracts”.

The code and the tests are together now.

After all, tests are “the contracts to the code” in some way.

They work when “static typing” fails.

I’ll remind you the right words of Roman Yankovsky:

"To some extend, unit-tests and static typing solve the same tasks. This is probably the reason why tests were adopted in areas where static typing is not available.

What is static typing? This is the code requirements description the conformity to which the code is checked by the compiler. What are unit tests? Again, these are the requirements the code should confirm with.

This understanding of unit tests eliminates the antilogies. What is the starting point of developing on languages with static typing? Types description! What is the starting point of developing on TDD? Tests writing! Thus, architecture first is suitable in both cases.

It looks not obvious and confusing, but this is due to underdeveloped means of language we use. However, the way how we describe the requirements to the input and output data of some function makes no difference, since we only describe the TYPE."

http://programmingmindstream.blogspot.ru/2013/11/tdd_28.html?showComment=1386063749826#c6337489882377572276

Actually, the idea was developed there.

The idea is not new.

Barbara Liskov developed a similar one.

In one separate case I “lived the idea out”.

In closing, I’ll refer to a simple thought I like.

If the code may be tested easily, it has to be tested.

It is great when tests are put together with the code or encapsulated in it.

In this case tests are not only used for checking, but also as examples of code use as well as the real demonstration of boundary conditions.

If you make “one more step”, than you will see the base requirements in the combination of code, tests and specification.

Let me also recommend you a book by Barbara Liskov and John Guttag – “Abstraction and Specification in Program Development”.

This is one of my favorite books.

Комментариев нет:

Отправить комментарий