transformed  into  our  internal  representation.  The 
transformation  uses  Lodash  (Lodash)  library  as  a 
functional  paradigm  helper.  JS  test  generator  is 
executed on this internal representation and prepares 
the textual output, which then will be saved in the test 
files.  Test  generation  itself  consists  of  three  stages 
and is repeated for each class. All members of a given 
class  are  considered,  also  those  inherited  from 
superclasses,  as  all  generalization  hierarchy  is 
traversed during test generation.  
Firstly, the class under test from the source code 
is  imported  into  the  unit  test  file.  Then,  for  each 
attribute which has type defined in the class diagram, 
a separate test is created. Unlike (Pires et al., 2008), 
we  test  the  code  written  in  a  dynamically  typed 
language, so types have to be determined in runtime. 
Thus, in every test an instance of the tested class is 
created.  In  the  second  line  of  each  test  there  is  a 
comparison of types. We write an assertion to check 
if the type of the implemented attribute is the same as 
in the UML model. The type from the UML model is 
read  from  our  internal  project  representation  and 
hardcoded in the test code. The actual attribute from 
implementation  is  obtained  dynamically  from  the 
previously created instance of the class under test. To 
check its type, we use JS typeof operator. 
The last stage of class test generation is similar. 
Tests  are  generated for  each  method, for which  the 
return  type  was  defined  and  is  a  primitive  JS  type. 
Again, the type returned by the implemented method 
can  be  obtained  only  dynamically,  so  the  method 
needs to be executed within the test. An instance of 
the class is created in  every such test. Moreover, to 
execute  a  method,  correct  arguments  need  to  be 
provided. We  achieve this by defining mock-ups of 
all parameters. The only worth information is the type 
of  the  return  value,  so  we  can  pass  any  values  of 
arguments  of  expected  types.  We  randomly  select 
values of parameters using Chance (Chance) library. 
As  the  tests  have  to  be  repeatable,  we  define  a 
constant  seed  at  the  first  usage  of  this  library.  The 
result of method execution is assigned to a variable. 
Finally, the assertion checks whether the type of this 
variable  is  compliant  with  the  hardcoded  type 
obtained from the class diagram. 
4.3  Modifications of Algorithm based 
on Activity Diagrams 
Our adaptation of the method proposed by (Kurth et 
al., 2014) is also very different from the original. The 
aforementioned  paper  describes  only  test  data 
generation,  while  we  propose  a  fully  automatic 
approach to unit tests generation. Here we assume the 
correctness  of  the  input  diagrams  again.  The 
constraint solver we use, Constrained (Constrained), 
supports only integer variables, so we assume that all 
variables  in  the  input  activity  diagram  are  integers. 
We also expect that the initial node of the diagram is 
related to a note with a method signature. As in the 
original solution (Kurth et al., 2014), all actions can 
have  notes  with  their  local  postconditions,  possibly 
with  @pre  marks,  and  guards  can  be  placed  on 
control  flows.  All  constraints  should  be  written 
according  to  the  specified  grammar  (Małkiewicz-
Błotniak, 2020) for the subset of OCL language. Test 
conditions  are  extracted  automatically  from  the 
activity diagram. 
Similarly  to  the  adaptation  described  in  the 
previous section, we transform the StarUML project 
into our own internal representation. Then, test cases, 
which will be stored in test files, are obtained. Here 
we use our adaptation of (Kurth et al., 2014) method. 
Firstly, it is necessary to generate all possible paths in 
the graph representing the input activity diagram. We 
use Graphlib (Graphlib) library to perform operations 
on the graph. All paths are found using the modified 
version of DFS algorithm. The authors of the original 
method  also  used  modified  DFS,  but  their 
modification  focused  on  infeasible  path  elimination 
(Kurth et al., 2014), while our version of DFS ensures 
that all of the paths are found, even if they have some 
nodes  in  common.  We  achieve  this  by  marking 
currently  processed  node  on  the  stack  as  unvisited 
after successfully finding a path. 
Once all paths are obtained, values of test data (i.e. 
parameters of the method, referenced attributes of the 
enclosing class  and  expected  return  value)  for  each 
path are generated. They are calculated with the help 
of  the  constraint  solver,  we  have  chosen  a  JS  tool 
named  Constrained  (Constrained).  This  solver  has 
some  limitations,  e.g.  it  supports  only  integer 
variables, but has also a great advantage – it always 
finds boundary values satisfying given condition, so 
no  separate  boundary  value  analysis  is  necessary. 
Before using the solver, the string representations of 
constraints are parsed. We have defined a simplified 
grammar of a subset of OCL language, which can be 
found in (Małkiewicz-Błotniak, 2020). The parser for 
this grammar is implemented with the help of Nearley 
(Nearley) library and employs a lexer defined using 
Moo (Moo) library. To analyze abstract syntax trees, 
we use functional paradigm utilities provided by the 
Lodash  (Lodash)  library.  All  new  variables  and 
constraints  are  added  to  the  Constrained  solver.  As 
this tool supports only non-strict inequalities, we have 
added  a  transformation  that  allows  for  usage of  all 
types  of  inequalities  on  the  input  diagram.  Strict