TAFJ UnitTestFramework
TAFJ UnitTestFramework
Doc u m e n t Hist o r y
Revi si o
Dat e Ame n d e d Na m e De s c r i p t i o n
n
1 Sep 11 2015 T. Aube r t Initial version
2 Nov 5 2015 T. Aube r t New function ali ti e s adde d
M.
19 12 th April 2021 R21 AMR review
Siranje evi
2
TAFJ U nit Test F r a m e wo rk
Copyri g h t
Pleas e includ e your na m e , comp a n y, addr e s s, and telep h o n e and fax num b e r s , and email add r e s s if
applica bl e. TAFJdev@t e m e n o s . c o m
Intro d u c t i o n
The Unit Test Fra m e w o r k (UTF) helps develop e r s and test e r s to validat e that the code is doing what it is
inten d e d to do. The version 1.0 was storing bina ry inform a ti o n s, and was requiri n g a specific editor to
modify a test s. Despit e that the “binary” way of storing in form a ti on s was a good appr o a c h for huge sets
of infor m a t i o n s, the downsi d e s wer e too big, and a full re- engin e e r i n g has been done to get to the
Version 2.
The Version 2 still have the recor di n g facility, and this is much easi er to maint ai n the test as this ends up
being “just” jBC code. Howeve r , unlike EasyQA, the r e is only one file per test and the full Eclipse
4
TAFJ U nit Test F r a m e wo rk
launc hi n g facilities are still availa bl e. This docu m e n t aims to desc ri b e in det ails how this all work. You
will notice that the r e is no “deploym e n t ” section as everyt hi n g is emb e d d e d in the existin g runti m e /
com pile r / eclips e plugi ns.
TUT Overvi e w
A .tut file is a jBC file. Howev e r , the exten sio n .tut is nec e s s a r y to help the differ e n t tools to discove r the
tests.
A tut is not a SUBROUTINE, neith e r a PROGRAM nor a FUNCTIO N. This is a
“TESTCASE”.
So every tuts are star tin g with TESTCASE < n a m e of test > as show n here :
We could wond e r why this could not be a simple SUBROUTINE or PROGRAM, and late r in the docu m e n t ,
everyt hi n g will make sens e. For now, we simply nee d to know that a UnitTe st is decla r e d in the code with
TESTCASE.
Then, this is just jBC, anyt hi n g jBC can do can be done in a tut. Howeve r, this would not be really useful
to stop at that point, and this is her e that the TESTCASE decla r a t i o n sta rt s to make sens e. In fact, by
using TESTCASE, this will auto m a t i c a lly add a low- level refer e n c e in the code. This refer e n c e is “UTF”.
IN the next cha pt e r , we will desc ri b e all the met ho d s the UTF refer e n c e (object) is offerin g. We will call
it a “com po n e n t ” to make the readi n g more jBC orient e d , but in fact this is a real Object.
So we know now that a Teme no s Unit Test is a file with the “tut” exten sio n that this is writt e n in jBC and
that the decla r a t i o n is “TESTCASE” bec a u s e we need the “UTF Compo n e n t ” which will help us doing a
test. So let's look in det ails at this Compo n e n t .
First, this is a low- level compo n e n t , sitting into the runti m e , so don't try to find som et hi n g like
UTF.com p o n e n t on your disk, it doesn' t exists. Then, this com po n e n t is auto m a t i c ally “her e”. No need to
do a $USING or what s o ev e r , it sits behind your code. You can consid e r it as a lang u a g e exten sio n. Let's
explor e all thes e exte n si on s. The full list :
Global methods :
UTF.runTest()
UTF.setTarget(<subroutine>)
UTF.setDescription(<a_description>)
UTF.addParam(<variable_or_value>)
UTF.setRecord(<table>, <id>, <record>)
UTF.ignoreMissingStubs()
Stubs methods :
UTF.addStub(<subroutine>)
UTF.setRepeatable(<a_stub_ref>)
UTF.addStubParam(<a_stub_ref>, <in_value>, <out_value>)
UTF.addStubCommonChange(<a_stub_ref>, <common>, <out_value>)
UTF.addStubCacheChange(<a_stub_ref>, <bucket_name>, <key>, <out_value>)
UTF.addStubWriteDone(<a_stub_ref>, <table>, <id>, <out_record>)
UTF.addStubPropertyChange(<a_stub_ref>, <component>, <property>, <value>)
UTF.setStubReturnValue(<a_stub_ref>, <value>)
Assertions methods :
UTF.assertEquals(<value1>, <value2>)
UTF.assertContains(<value1>, <value2>)
UTF.assertTrue(<value>)
UTF.assertFalse(<value>)
UTF.assertGreaterThan(<value1>, <value2>)
UTF.assertLessThan(<value1>, <value2>)
UTF.assertStartsWith(<value1>, <value2>)
UTF.assertEndsWith(<value1>, <value2>)
UTF.assertMatches(<value1>, <mask>)
UTF.getRecord(<table>, <id>)
Modifiers methods :
UTF.any()
UTF.matches(<mask>)
UTF.same()
IF SYSTEM(47) THEN …
UTF.ru nT e s t()
This met ho d simply launc h the test (call the routi n e define d by the setTa r g e t (...). This met ho d must be
called afte r the stub s definition (see next section)
If the routin e define d by the setTa r g e t is a FUNCTION, then UTF.ru nT e s t () will ret u r n the value of the
function.
Eg :
ret.value = UTF.runTest()
UTF.re s e t ()
Clea r compl e t elly the cont ext (COMMO N, prope r t i e s, recor d s, static cach e, mocke d SYSTEM and
par a m e t e r s ) .
UTF.s av eC o n t e x t()
Save (in- mem o r y) a snap s h o t of the curr e n t COMMO N, prope r ti e s, recor d s, static cach e and par a m e t e r s .
UTF.re s t o r e C o n t e x t()
Restor e the previously saved cont ext (see saveCo n t e x t()). This is also settin g all the stub s invoca tion
count e r back to 0.
TESTCASE TestDATE.TIME
UTF.setTarget("TEST.DATE.TIME")
date = ''
UTF.addParam(date)
time = ''
UTF.addParam(time)
8
TAFJ U nit Test F r a m e wo rk
timedate = ''
UTF.addParam(timedate)
timestamp = ''
UTF.addParam(timestamp)
sys12 = ''
UTF.addParam(sys12)
UTF.runTest()
UTF.assertEquals(date, 117)
UTF.assertEquals(time, 11655)
UTF.assertEquals(timedate, "03:14:15 26 APR 1968")
UTF.assertEquals(timestamp, -53127945)
UTF.assertEquals(sys12, 11655000)
END
UTF.re s e t D a t e Ti m e ()
Reset (canc el any setD a t e() or setTim e()) the dat e / time to the curr e n t / defa ul t one.
This will affect READU, CALL F.READU(), MATREADU, CALL F.MATREADU() and READVU
Here is a full code showi ng a routin e and its test :
SUBROUTINE GET.LOCK(REC)
OPEN "FBNK.CURRENCY" TO F.FILE ELSE NULL
DIM AR(100)
MATREADU AR FROM F.FILE, "USD" LOCKED
REC = "MATREADU RECORD LOCKED / "
END
READU R FROM F.FILE, "USD" LOCKED
REC := "READU RECORD LOCKED / "
END
READVU R FROM F.FILE, "USD", 2 LOCKED
REC := "READVU RECORD LOCKED / "
TESTCASE TestGET.LOCK
UTF.setTarget("GET.LOCK")
REC = ''
UTF.addParam(REC)
myRecord = UTF.setRecord("FBNK.CURRENCY", "USD", "Currency Record")
UTF.lockRecord(myRecord)
UTF.runTest()
UTF.assertEquals(REC, "MATREADU RECORD LOCKED / READU RECORD LOCKED / READVU RECORD LOCKED /
RECORD LOCKED / RECORD LOCKED")
END
Stubs met ho d s
10
TAFJ U nit Test F r a m e wo rk
UTF.se t R e p e a t a b l e ( < a _ s t u b_ r ef > )
Nor m a lly, when the invocatio n of a sub is done, this one will not be re- use d. As an exam pl e, if the tar g e t
is calling 2 time s DO.SOMETHIN G, the n you should have 2 stubs (you should put 2
UTF.ad d S t u b (“DO.SO M ET HI NG”). Howev e r, by specifying that the stub is rep e a t a bl e , this save you to
decl a r e it twice. A typical routin e in T24 which can be “repe a t a b l e” is “CDT”.
The section desc ri bin g how the stub s are found (late r in this docu m e n t ) will cert ai nly bring som e lights.
STUB = UTF.addStub("CDT")
UTF.addStubParam(STUB, '', '')
UTF.addStubParam(STUB, "20130523", "20130524")
UTF.addStubParam(STUB, "+1C", "+1C")
When the tar g e t calls CDT('', 2013 0 6 2 3 , “+1C”), the n the 2 nd par a m e t e r cha n g e s to 2013 0 6 2 4 . The test s
nee d s to know which one to take. This explain the in- par a m e t e r .
Note that STUB is just a varia bl e nam e, you can put anyt hin g you want.
MY.FIRST.STUB = UTF.addStub("CDT")
UTF.addStubParam(MY.FIRST.STUB, '', '')
. . .
If your com m o n is an DIMm e d arr ay (eg R.NEW), you will pass an arr ay as new value :
DIM stubCommon(500)
UTF.ad d S t u b C a c h e C h a n g e ( < a _ s t u b_ r ef > , < b u c k e t_ n a m e > , <k ey > , <o u t_valu e > )
Sam e app ro a c h as for the com m o n, app a r t from the fact that you are not passi n g the com m o n itself, but
the Bucke t nam e and the key.
Doing som e t hi n g like that :
UTF.ad d S t u b W ri t e D o n e ( < a _ s t u b_ r ef > , < t a b l e > , < i d > , <ou t_r e c o r d > )
Exactly the sam e as the previous one, but for dat a b a s e .
So doing
is the sam e as if the routin e wer e doing a F.WRITE. Of cour s e, the data b a s e is not touch e d , but this is “as
if ...”
Retu r n the num b e r of time a stub got invoke d durin g the test
12
TAFJ U nit Test F r a m e wo rk
exa m pl e (check that our stub got invoke d 2 time s) :
NB.STUB = UTF.getNbInvocation(STUB)
UTF.assertEquals(2, NB.STUB)
Stub Condition s
The way stub s are select e d is, by nat u r e , base d on par a m e t e r s . These para m e t e r s can have modifie rs
(see specific cha pt e r) or can be set as UTF.any(). Howev e r, this is some ti m e s nec e s s a r y to specify whe n a
STUB is eligible base d on COMMON varia bl e s or Prope r ti e s.
There are 2 met h o d s to crea t e such condition s :
UTF.ad d S t u b C o m m o n C o n d i tio n( < a _ s t u b_ r e f > , A.COMMO N, value)
eg :
UTF.a d d S t u b C o m m o n C o n di ti o n( STUB, COMI, “1234”)
or
UTF.ad d S t u b P r o p e r t y C o n di tio n( < a _ s t u b_ r ef > , “Compo n e n t . n a m e ” , “Prope r t y”, value)
eg :
UTF.ad d S t u b P r o p e r t y C o n di ti on( STUB, “EB.Syst e m T a bl e s”, “Comi”, “1234”)
The value can be wra p p e d by all the modifier s.
Eg :
UTF.a d d S t u b C o m m o n C o n di ti o n( STUB, COMI, UTF.lessT h a n( 1 2 3 4 ))
Which mea n s :
This STUB is valid if the COMMON variabl e COMI is less than 1234 at the mom e n t of the call.
IO Met h o d
IO Keywor d s (OPEN, READ, WRITE, DELETE, MATREAD, MATWRITE, OPENS EQ, READSEQ,
WRITESEQ) but also their corr e s p o n di n g met ho d s (F.READ, CACHE.READ, F.WRITE, F.MATWRITE,
F.MATREAD, F.DELETE) are all base d on a “in- mem o ry stor a g e).
There is no need to STUB a F.READ, neith e r a F.WRITE, .... They will auto m a ti c ally use/u p d a t e this in-
me m stor a g e .
Now, before runni n g a test (runTe s t()), you will use the UTF.se t R e c o r d() met ho d, and after the test, if
you want to verify a reco r d, you will used the UTF.ge t R e c o r d () met ho d.
In the case of the SEQu e n ci al files, you will use the exact sam e mech a n i s m , but the ID will have to stay
empty (“”).
Here is an exam pl e of SEQUE NC IAL acce s s :
The Targe t :
SUBROUTINE DO.SEQ(FIRSTLINE)
OPENSEQ "/home/user/TOTEST", "MY.FILE" TO F.SEQ.FILE THEN
READSEQ FIRSTLINE FROM F.SEQ.FILE ELSE NULL
WRITESEQ "One Value" ON F.SEQ.FILE THEN NULL
WRITESEQ "Another Value" ON F.SEQ.FILE THEN NULL
END
END
END
Ass e r t i o n s me t h o d s
These met h o d s are obvious and talking by the m s elv e s. If one of thes e met ho d is evalua t e d as “false”, the
test will be in “failur e”. Pleas e refer to the “repo r ti n g ” and “eclipse” chap t e r s for more det ails.
One small not e on the para m e t e r s .
At the begin ni n g of the docu m e n t , we mentio n e d that the par a m e t e r s shoul d be put as variabl e s and not
const a n t . Imagi n e that you wrot e
UTF.addParam("7")
Inst e a d of
P1 = "7"
UTF.addParam(P1)
UTF.assertEquals(P1, "7")
14
TAFJ U nit Test F r a m e wo rk
UTF.as s e r t N o t E q u a l s ( < v a l u e 1 > , <valu e 2 > )
TRUE if <val u e 1 > is differe n t tha n < v a l u e 2 > , FALSE othe r wi s e
UTF.assertMatches(P1, "3N4X")
P1 = "123ABCD" : TRUE
P1 = "123ABC" : FALSE
Modifi e r s
This section should in fact be linked to the Stub s, but for clarity of the docu m e n t s let's keep it at the end.
Modifier s are infor m a t i o n s you can pass as argu m e n t s to the stub definition s or the stub s conditions.
When a test is runnin g, and the targ e t is makin g a CALL, the UTF is looking for the best mat c hi n g stub.
The first absolut e test is the stub nam e (the SUBROUTINE nam e must matc h EXACTELY the call
-obvious- ). Then, we will check the par a m e t e r s .
Exam pl e :
UTF.any()
Do not conside r the par a m e t e r to elect the stub as a good stub for a given CALL.
UTF.s a m e ()
UTF.sa m e () is not a modifier as such. It is define d for the out- par a m e t e r . This is basically saying that the
out par a m e t e r ends up having the sam e value as the in. As an exa m pl e, this par a m e t e r :
UTF.addStubParam(STUB, UTF.matches("2N"), UTF.same())
Would not cha n g e the par a m e t e r if this is a 2 digit num b e r , and of cour s e, if ther e is no bett e r matc h (eg
UTF.addStubParam(STUB, "12", "twelve"))
The following modifiers do obviously not nee d any explan a t io n s :
16
TAFJ U nit Test F r a m e wo rk
UTF.isTru e( < v a l u e > )
Creat i n g a ne w UnitT e s t
From scra t c h
Crea ti n g a test is not any differ e n t tha n cre a ti n g a new SUBROUTINE : In Eclipse, Right- click on the
direct o ry whe r e you want to cre a t e your test, selec t
New - > T24 routine / component / testcase
And auto m a t i c a lly a new TestC a s e , with a tem pl a t e will be crea t e d as follows :
18
TAFJ U nit Test F r a m e wo rk
Then this is just
a matt e r to edit /
save / run.
Editor
Helpe r s
When editing a
TESTCASE, you
have som e
options in the
cont ext u al men u
of your editor.
Righ- click on the
editor (whe r e
you want to add
som e t hi n g). You
will have the “Unit Test Fra m e w o r k ….” menu availabl e with 2 options : “Setti n g the targ e t ...” and
“Adding a stub...”
Adding a stub
Adding a stub is done the sam e way as settin g the targ e t . The code will be gene r a t e d like this ( for
AC.API.EbUpdateAcctEntFwd )
The default values are UTF.a ny() for the IN par a m and UTF.sa m e () for the OUT para m . You will nee d to
chan g e thes e values accor di n g to your test case.
The prec e d e n t chap t e r is mentio ni n g this dialog box, and her e is a desc ri ptio n of how it works.
IMPORTA NT : The con t e n t s of thi s dial o g box IS NOT THE SOURCE file s , but th e co m p i l e d on e .
This me a n s you don't ne e d to hav e all so u r c e s to be abl e to sel e c t a routi n e . Just have th e jars
in your pre c o m p i l e dire c t o r y .
There is a tool to conve r t tut version 1.0 to the new 2.0 version.
In the TAFJ/bin direct o ry, you will find the tConve r t T e s t (.bat file).
The synt ax is :
If the new file is omitt e d, the <file_to_conve r t > will be overri d d e n by the new one.
20
TAFJ U nit Test F r a m e wo rk
Compiling, and distrib u ti n g tests
A test acts as a jBC PROGRAM or SUBROUTIN E. There compile the sam e way, a Class is gene r a t e d , this
class can be put in a jar.
Run n i n g te s t s
In console mode
Runni n g a test is not very differ e n t as runni n g a prog r a m . Ther e is only one flag to specify : “-test”. This
flag is nece s s a r y as the class gene r a t e d is prefixed with “test c a s e .” to not conflict with the tar g e t
subr o u t i n e .
To resu m e :
exa m pl e : tRun MY.SUBROUTINE will try to run the class MY_SUBROUTINE_cl.cla s s which is wrong.
You have to run :
So this is
You can also run your test by specifying the full file na m e .
Exam pl e :
You can also specify a direc t o ry. In that case, all test s in this direct o ry will be run
Exam pl e :
Exam pl e :
Retu r n value
In console mode, the exit value of tRun will be 1 if one or mor e test are failing. A succe s s value is 0.
Note that a failure can be som e t hi n g else than a wron g asse r t io n. It can be a call to a non existin g
routin e, a test without asse r ti o n, or any kind of techni c al failur e.
22
TAFJ U nit Test F r a m e wo rk
In Eclipse
Running a test in eclipse consist of selecting
Autom a t ic ally, a new window will open showing the tests and their result s :
This view will show all possible infor m a t io n s reg a r di n g the differ e n t tests.
One *.tut file can cont ai n s multipl e test s (UTF.ru nT e s t (..). In that case, this singl e tut file will be
pres e n t e d as multiple test, with a count e r (2 nd colum n). You can drill- down (open) the line to see the
differ e n t asse r t io n s.
Also, the last colum n is showin g the code cover a g e of the tar g e t . You can right- click on a line and you
will have the choice to
1) open the tut file
2) open the targ e t with the code cover a g e highlight s (or doubl e click)
3) open the targ e t .
Also, by doubl e- clicking on an asse r ti o n, the tut file will be open and the focus will be given to the
asse r ti o n line.
24
TAFJ U nit Test F r a m e wo rk
Colors mea ni n g
Everyt hi n g which is Gree n conce r n s the test itself. If this is a “light” gree n, this me a n s that thes e are
lines cover e d by this test only. The num b e r of line is folowing the perc e n t a g e . (eg : 53% +2 mea n s : Code
cover a g e of this test is 53 %, and 2 lines are cover e d just by this test). This allow the develop e r to quickly
see what is the ben efit of this test in ter m of code cover a g e .
The gray lines are lines cover e d by the othe r test s for the sam e targ e t .
The color
sche m e in the
Result view is
the sam e as the
one in the
editor.
Here is an
exa m pl e of
what you will
see when
doubl e- clicking
on a test (not an
asse r ti o n) or by
choosin g “Open
Targe t with
Code Cover a g e ”
Line 32 - > 40
are not touc h e d
by this test, but
are touc h e d by
som e othe r
tests
Line 17 → 26 are cover e d by this test and som e othe r test s.
Line 27 /28 are touch e d by this test and this is the only test touchi n g them .
You will see that som e lines (29, 36, 38) are not highligh t e d . This is beca u s e the r e is no equivale n t of
thes e lines in java, and the runti m e is neve r touc hi n g the m. The count of total line knows that and the
cover a g e ratio of this tar g e t is 100%. This will be resolve d in futur e versions.
Rep o r t i n g
You can generate junit types of reports by specifying it in the properties file with the following keys :
temn.tafj.utf.generate.results = true
By default, the repor t s (xml files) are gene r a t e d in
<tafj_home>/data/<projectName>/testResults/
25 Teme n o s Application Fra m e w o r k Java – (TAFJ)
TAFJ U nit Test F r a m e wo rk
You can chan g e this defa ul t location with the key
temn.tafj.utf.results.directory = …
26