Sourcing global variables in unit tests

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Sourcing global variables in unit tests

phofman
Hi,

Please is there any way to source a script defining global variables in
unit test (%!test) and have these available in functions called within
the test?

This works:

%!test
%! global dataDir = '/tmp';
%! func_using_global_dataDir();

But this does not work, dataDir is not available in
func_using_global_dataDir():


%!test
%! source 'consts.m';
%! func_using_global_dataDir();

Where script consts.m contains
global dataDir = '/tmp';


Yet when consts.m is sourced from another script (in the main code, not
the unit script %! section), the global vars defined in consts.m are
available in the script as well as all functions called by the script.

Tested with Octave 4.2.2 in Linux.

Thanks a lot for any help or suggestion how to let a unit test
source/load a script with global constants used in functions tested by
that test. Otherwise the scope of unit tests would be severely limited
as all global variables (constants) used in the tested functions would
have to be redefined in the unit test.

With regards,

Pavel.


Reply | Threaded
Open this post in threaded view
|

Re: Sourcing global variables in unit tests

phofman
Dne 15. 04. 19 v 18:29 Pavel Hofman napsal(a):

> Hi,
>
> Please is there any way to source a script defining global variables in
> unit test (%!test) and have these available in functions called within
> the test?
>
> This works:
>
> %!test
> %! global dataDir = '/tmp';
> %! func_using_global_dataDir();
>
> But this does not work, dataDir is not available in
> func_using_global_dataDir():
>
>
> %!test
> %! source 'consts.m';
> %! func_using_global_dataDir();
>
> Where script consts.m contains
> global dataDir = '/tmp';
>
>
> Yet when consts.m is sourced from another script (in the main code, not
> the unit script %! section), the global vars defined in consts.m are
> available in the script as well as all functions called by the script.
>
> Tested with Octave 4.2.2 in Linux.

It may be related to this problem:

%!test
%! global A;
%! A = 1;
%! funcX_calling_funcY();

This global variable A is available in function funcY() called from
funcX_calling_funcY()

But when defining global vars with a one-liner:


%!test
%! global A = 1;
%! funcX_calling_funcY();


the global var A is NOT available in funcY(), ONLY in funcX_calling_funcY().

The same holds if the sourced/included file defines the globals with two
lines:

consts.m:
global A;
A = 1;


instead of the one-liner

global A = 1;


But the oneliner global works OK in regular code, just unit tests have
this second-level problem.

Again, my Octave is a bit older 4.2.2.

Thanks a lot.

Pavel.


Reply | Threaded
Open this post in threaded view
|

Re: Sourcing global variables in unit tests

siko1056
On Tue, Apr 16, 2019 at 1:47 AM Pavel Hofman <[hidden email]> wrote:
Dne 15. 04. 19 v 18:29 Pavel Hofman napsal(a):
> Hi,
>
> Please is there any way to source a script defining global variables in
> unit test (%!test) and have these available in functions called within
> the test?
>
> This works:
>
> %!test
> %! global dataDir = '/tmp';
> %! func_using_global_dataDir();
>
> But this does not work, dataDir is not available in
> func_using_global_dataDir():
>
>
> %!test
> %! source 'consts.m';
> %! func_using_global_dataDir();
>
> Where script consts.m contains
> global dataDir = '/tmp';
>
>
> Yet when consts.m is sourced from another script (in the main code, not
> the unit script %! section), the global vars defined in consts.m are
> available in the script as well as all functions called by the script.
>
> Tested with Octave 4.2.2 in Linux.

It may be related to this problem:

%!test
%! global A;
%! A = 1;
%! funcX_calling_funcY();

This global variable A is available in function funcY() called from
funcX_calling_funcY()

But when defining global vars with a one-liner:


%!test
%! global A = 1;
%! funcX_calling_funcY();


the global var A is NOT available in funcY(), ONLY in funcX_calling_funcY().

The same holds if the sourced/included file defines the globals with two
lines:

consts.m:
global A;
A = 1;


instead of the one-liner

global A = 1;


But the oneliner global works OK in regular code, just unit tests have
this second-level problem.

Again, my Octave is a bit older 4.2.2.

Thanks a lot.

Pavel.


Can you provide a small, comprehensible, and complete test within a ZIP file?   Maybe this already works with Octave 5.1.0.

Otherwise, why don't you call a function "my_consts" on the workspace, providing the necessary test constants?

function [const_a, const_b, ....] = my_consts ()
const_a = pi;
#...
endfunction

In some test context:

%!test
%! [~,b] = my_consts ();
%! ## Some useful test

HTH,
Kai 


Reply | Threaded
Open this post in threaded view
|

Re: Sourcing global variables in unit tests

phofman
Hi Kai,

Dne 16. 04. 19 v 5:26 Kai Torben Ohlhus napsal(a):
>
>
> Can you provide a small, comprehensible, and complete test within a ZIP
> file?   Maybe this already works with Octave 5.1.0.

This very simple example - first test works, the second one fails.

function [A, B] = f1Direct()
   global A;
   global B;
endfunction

%!test
%! global A;
%! A = 2;
%! global B;
%! B = 3;
%! [o1, o2] = f1Direct();
%! expected = [2, 3];
%! assert([o1, o2], expected);

%!test
%! global A = 1;
%! global B = 2;
%! [o1, o2] = f1Direct();
%! expected = [1, 2];
%! assert([o1, o2], expected);


 >> test f1Direct.m
***** test
  global A = 1;
  global B = 2;
  [o1, o2] = f1Direct();
  expected = [1, 2];
  assert([o1, o2], expected);
!!!!! test failed
ASSERT errors for:  assert ([o1, o2],expected)

   Location  |  Observed  |  Expected  |  Reason
     (1)           2            1         Abs err 1 exceeds tol 0
     (2)           3            2         Abs err 1 exceeds tol 0


>
> Otherwise, why don't you call a function "my_consts" on the workspace,
> providing the necessary test constants?

My consts.m defines tens of constants, all used throughout my project
https://github.com/pavhofman/nonlinear-compensation/blob/master/octave/consts.m 
.

I can imagine returning a struct with all the constants

const().CONST1
const().CONST2
const().CONST3

Using this would avoid the "global" call overhead at the same time. But
on the other hand it introduces the overhead with returning/accessing a
large struct.

Thanks,

Pavel.


Reply | Threaded
Open this post in threaded view
|

Re: Sourcing global variables in unit tests

siko1056
On Tue, Apr 16, 2019 at 9:10 PM Pavel Hofman <[hidden email]> wrote:
Hi Kai,

Dne 16. 04. 19 v 5:26 Kai Torben Ohlhus napsal(a):
>
>
> Can you provide a small, comprehensible, and complete test within a ZIP
> file?   Maybe this already works with Octave 5.1.0.

This very simple example - first test works, the second one fails.

function [A, B] = f1Direct()
   global A;
   global B;
endfunction

%!test
%! global A;
%! A = 2;
%! global B;
%! B = 3;
%! [o1, o2] = f1Direct();
%! expected = [2, 3];
%! assert([o1, o2], expected);

%!test
%! global A = 1;
%! global B = 2;
%! [o1, o2] = f1Direct();
%! expected = [1, 2];
%! assert([o1, o2], expected);


 >> test f1Direct.m
***** test
  global A = 1;
  global B = 2;
  [o1, o2] = f1Direct();
  expected = [1, 2];
  assert([o1, o2], expected);
!!!!! test failed
ASSERT errors for:  assert ([o1, o2],expected)

   Location  |  Observed  |  Expected  |  Reason
     (1)           2            1         Abs err 1 exceeds tol 0
     (2)           3            2         Abs err 1 exceeds tol 0


>
> Otherwise, why don't you call a function "my_consts" on the workspace,
> providing the necessary test constants?

My consts.m defines tens of constants, all used throughout my project
https://github.com/pavhofman/nonlinear-compensation/blob/master/octave/consts.m
.

I can imagine returning a struct with all the constants

const().CONST1
const().CONST2
const().CONST3

Using this would avoid the "global" call overhead at the same time. But
on the other hand it introduces the overhead with returning/accessing a
large struct.

Thanks,

Pavel.


Dear Pavel,

Thank you for the example.  This makes your problem clearer to me.  You should avoid the syntax

   global A = 1;

This is a convenient Octave extension, that does not exist in Matlab.  The problem is, that if a global variable A already exists, any consecutive initializations will be ignored (see [1]):

  global A = 1;
  global A = 2;  % ignored A == 1

or

  global A;
  global A = 3; % ignored A == [];

Thus avoid this Octave extension where not appropriate and you function (and https://github.com/pavhofman/nonlinear-compensation/blob/master/octave/consts.m) must be changed to a global declaration line and a second initialization line in consts.m.  For example:

function [A, B] = f1Direct()
   global A;
   global B;
end

%!test
%! global A;
%! A = 2;
%! global B;
%! B = 3;
%! [o1, o2] = f1Direct();
%! expected = [2, 3];
%! assert([o1, o2], expected);

%!test
%! global A B;
%! A = 1;
%! B = 2;
%! [o1, o2] = f1Direct();
%! expected = [1, 2];
%! assert([o1, o2], expected);

Best,
Kai



Reply | Threaded
Open this post in threaded view
|

Re: Re: Sourcing global variables in unit tests

John W. Eaton
Administrator
In reply to this post by phofman
On 4/16/19 8:10 AM, Pavel Hofman wrote:

> Hi Kai,
>
> Dne 16. 04. 19 v 5:26 Kai Torben Ohlhus napsal(a):
>>
>>
>> Can you provide a small, comprehensible, and complete test within a
>> ZIP file?   Maybe this already works with Octave 5.1.0.
>
> This very simple example - first test works, the second one fails.
>
> function [A, B] = f1Direct()
>    global A;
>    global B;
> endfunction
>
> %!test
> %! global A;
> %! A = 2;
> %! global B;
> %! B = 3;
> %! [o1, o2] = f1Direct();
> %! expected = [2, 3];
> %! assert([o1, o2], expected);
>
> %!test
> %! global A = 1;
> %! global B = 2;
> %! [o1, o2] = f1Direct();
> %! expected = [1, 2];
> %! assert([o1, o2], expected);
>
>
>  >> test f1Direct.m
> ***** test
>   global A = 1;
>   global B = 2;
>   [o1, o2] = f1Direct();
>   expected = [1, 2];
>   assert([o1, o2], expected);
> !!!!! test failed
> ASSERT errors for:  assert ([o1, o2],expected)
>
>    Location  |  Observed  |  Expected  |  Reason
>      (1)           2            1         Abs err 1 exceeds tol 0
>      (2)           3            2         Abs err 1 exceeds tol 0
>

Initialization of global variables only happens once.  You can make your
tests work if you clear the global values at the end of each test:

%!test
%! global A = 2;
%! global B = 3;
%! [o1, o2] = f1Direct();
%! expected = [2, 3];
%! assert([o1, o2], expected);
%! clear -global A B

%!test
%! global A = 1;
%! global B = 2;
%! [o1, o2] = f1Direct();
%! expected = [1, 2];
%! assert([o1, o2], expected);
%! clear -global A B

I think this is better anyway, as it makes each test stand alone and
avoids leaking global values into your environment after the tests are run.

It should be possible to initialize and clear the global values in a
separate script or function if needed.

jwe


Reply | Threaded
Open this post in threaded view
|

Re: Sourcing global variables in unit tests

phofman
In reply to this post by siko1056
Hi Kai,

Dne 16. 04. 19 v 15:34 Kai Torben Ohlhus napsal(a):

> On Tue, Apr 16, 2019 at 9:10 PM Pavel Hofman <[hidden email]
> <mailto:[hidden email]>> wrote:
>
> Thank you for the example.  This makes your problem clearer to me.  You
> should avoid the syntax
>
>     global A = 1;
>
> This is a convenient Octave extension, that does not exist in Matlab.  
> The problem is, that if a global variable A already exists, any
> consecutive initializations will be ignored (see [1]):

I see, thanks a lot for the info. It is a pitty such clumsy syntax is
the correct one. Honestly, I hesitate whether easier testing is worth
cluttering my code with the two-liners.

Best regards,

Pavel.


Reply | Threaded
Open this post in threaded view
|

Re: Sourcing global variables in unit tests

phofman
In reply to this post by John W. Eaton
Hi John

Dne 16. 04. 19 v 15:51 John W. Eaton napsal(a):
>
> Initialization of global variables only happens once.  You can make your
> tests work if you clear the global values at the end of each test:
>
>
> I think this is better anyway, as it makes each test stand alone and
> avoids leaking global values into your environment after the tests are run.
>

Thanks a lot for the info and best-practices. It is always good to learn
from those experienced.

Best regards,

Pavel.