http://www.perlmonks.org?node_id=1072994


in reply to Re: 'state' variables and unit testing
in thread 'state' variables and unit testing

Note that the equivalent to state vars is my $_cached = ..., not our $_cached = ..., so you are really comparing apples and oranges.

Yes 'state' is more like 'my', but I disagree about apples and oranges.

I am comparing two different caching approaches:

1) caching without 'state'. old way.

2) caching with 'state'. new way.

I am comparing (2) with (1) because both about caching and (2) is much easier to write/clearer than (1). Not because 'state' (not)similar to our/my.

and my $_cached isn't much easier/clearer than our $_cached = ... (except "our" var is visible in global namespace, but I use underscore in name to indicate that it's private).

But the problem is really something else: caching a value that depends on another value without taking this dependency into account is a bug

Not always.

In my example I've used $ARGV[0] to indicate something "global" (perhaps it's not clear in that particular example that @ARGV is not something that should be passed across call stack).

Other use of global things include:

%Config variables, $^O, $$, $ENV, locale, Versions of modules used, perl version, other application specific global flags and constants, which are immutable during process execution

More than that, those global var, actually, not necessary affect the result

For example I might have a code which calculates value on 64bit machine, and a slower code which calculates it on 32bit machine. Result should be same on both. And I can assume that 32bit version will work on 64bit as well and wish to test both in unit test running on 64bit machine

$cached_value{ is_64bit_machine() } ||= somelongfunction()
does not make sense in this case.

Also, even if there is no implicit global parameter, I might want to run function twice in test with other functions mocked different ways.

There are other uses of 'state' not related to caching

Something like this untestable too

sub global_sum { state $sum = 0; $sum += $_[0]; $sum; }
EDIT Anyway, if you suggest that function result should depend only on it's arguments, than mocking/stubbing is not needed at all. However that's not true, because we see mocking libraries exist for Perl.

Replies are listed 'Best First'.
Re^3: 'state' variables and unit testing
by moritz (Cardinal) on Feb 01, 2014 at 20:57 UTC
    Something like this untestable too

    To me it looks like a bug, not just untestable. Tests just reveal the bug.

    Let me rephrase my earlier point: APIs are testable or not testable; state variables are just a way to implement an API. state vars are a tool, and neither good nor evil; it's only misuse of state vars (and thus broken APIs) that makes stuff hard to test.

Re^3: 'state' variables and unit testing
by Jenda (Abbot) on Feb 02, 2014 at 02:06 UTC

    If you use a global for the caching you are doing it wrong. You allow anyone, anywhere to change the cached value. The right way before state was

    { my $_cached; sub whatever { $_cached //= ... } }

    Jenda
    Enoch was right!
    Enjoy the last years of Rome.

      It is very often useful to prevent people from accidentally violating encapsulation. It is usually a mistake to try to prevent people from intentionally violating encapsulation.

      Caching to a global variable is very often extremely handy when writing unit tests. Nobody accidentally modifies a global variables in another package. Nobody on my team even does that intentionally in Production code.

      And I've seen lots of really stupid things done to try to work around such attempts to prevent intentional violations of encapsulation. They are usually much worse than the behavior that was trying to be prevented.

      - tye        

        Unlike the solution using state the my in an enclosing block allows you to include controlled access to the cache.

        { my $_cache; sub withLongComputation { $_cache //= ...; ... } sub clearCache { undef $_cache; } }

        Jenda
        Enoch was right!
        Enjoy the last years of Rome.