Security is one of those exceptions to the rules about ThreadLocal. Almost
every implementation uses ThreadLocal to stash the current executing
context. Generally then there is a static class that gets the current
context. To test you should be able to just push your own mocked context
into the context. Not familiar with Shiro but I have done this with a few
other JAAS implementations.

-Jake


On Mon, Jul 25, 2016 at 5:33 PM Jinmei Liao <jil...@pivotal.io> wrote:

> Shiro relies heavily on ThreadLocal. GeodeSecurityUtils is a wrapper around
> Shiro. That's where all the static usage stems from. There is a trade-off
> if we pass some sort session object around: calling code would need to keep
> a reference of it and knows when/where/which to pass it down.
>
> On Mon, Jul 25, 2016 at 5:21 PM, Dan Smith <dsm...@pivotal.io> wrote:
>
> > IMHO static helper methods like those in Bytes are fine. We would never
> > want to substitute a mock implementation of one of those methods and they
> > don't have any collaborators.
> >
> > Just glancing at GeodeSecurityUtils, what I don't like is that it's
> relying
> > heavily on mutable static singletons (securityManager, postProcessor) and
> > thread locals. I think we should avoid using singletons and thread locals
> > in geode code, because it's too easy to leave junk lying around and it
> also
> > makes code hard to test. In this case maybe it would make more sense to
> > pass some sort of session object around?
> >
> > -Dan
> >
> > On Mon, Jul 25, 2016 at 4:26 PM, Kirk Lund <kl...@pivotal.io> wrote:
> >
> > > I was trying to describe any Java class in Geode which provides feature
> > > functionality that should be unit tested and integration tested, but is
> > > either currently implemented as a static class or which collaborates
> > with a
> > > non-trivial component currently implemented as a static class.
> > >
> > > I wasn't describing the simple static classes like Byte, StringUtils,
> > > IOUtils. These are trivial and not generally considered to be an
> > important
> > > collaborator requiring a mock.
> > >
> > > Using Mockito allows us to test the interaction between a class under
> > test
> > > and its collaborators. To facilitate this, we need the collaborator to
> be
> > > defined as an interface or abstract class.
> > >
> > > This specifically came about on my team because we have been unit
> testing
> > > GMSAuthenticator and lots of Client Commands that interact with
> > Integrated
> > > Security. We want to include unit tests to verify that the class under
> > test
> > > correctly interacts with its collaborators including Integrated
> Security
> > > (GEODE-17). For this reason, we want to transform GeodeSecurityUtil
> into
> > a
> > > non-static class that implements an interface that can be mocked.
> > >
> > > Hopefully everyone agrees that we need to make a concerted effort to
> > write
> > > lots of new fast-running unit tests that use mocks. This includes old
> and
> > > new code.
> > >
> > > In general, actual features implemented in static classes is common in
> > > Geode but it inhibits unit testing and thus should change as we
> refactor
> > > the code towards testability.
> > >
> > > This thread started as me describing a simple pattern to facilitate
> unit
> > > testing against static classes that for whatever reason, we want to
> hold
> > > off refactoring them but we don't want to hold off writing unit tests
> for
> > > them as an important collaborator class.
> > >
> > > Does this make sense? Is everyone on the same page regarding the value
> of
> > > unit testing and refactoring to testability & maintainability?
> > >
> > > -Kirk
> > >
> > > On Wednesday, July 20, 2016, Anthony Baker <aba...@pivotal.io> wrote:
> > >
> > > > Great discussion!  I’ve got two questions:
> > > >
> > > > 1) What do you think of Bytes.java
> > > > (:geode-core:com.gemstone.gemfire.internal.util)?  Is it a reasonable
> > use
> > > > of the Helper / Utility class pattern?  If not, how would you change
> > it?
> > > > Creating a new instance for each use seems inefficient and using a
> > > > Singleton is just a different way to do statics.
> > > >
> > > > 2) Is GeodeSecurityUtil of the same category as Bytes.java or is it
> > > > something more?
> > > >
> > > > Anthony
> > > >
> > > >
> > > > > On Jul 20, 2016, at 10:41 AM, Jinmei Liao <jil...@pivotal.io
> > > > <javascript:;>> wrote:
> > > > >
> > > > > Wouldn't the change be too intrusive? Now any class that uses
> > security
> > > > > would need to have this additional constructor, and if a no-arg
> > > > constructor
> > > > > is available, it's error prone that the class will be used without
> > > > > security, and if we delete the no-arg constructor, that would
> cause a
> > > lot
> > > > > of chain reaction.
> > > > >
> > > > > The usage of shiro itself is static (all the state information is
> in
> > > the
> > > > > thread), which, in my opinion is one of the selling points of Shiro
> > > since
> > > > > it greatly simplifies the usage of it. That's why GeodeSecurityUtil
> > > > follows
> > > > > this path. If no security is configured, calls into any of the
> method
> > > in
> > > > > GeodeSecurityUtil would be a no-op. And each VM would need only one
> > > > > instance of GeodeSecurityUtil so static makes sense here IMHO.
> > > > >
> > > > > For testing purposes, if you are not testing security in your test,
> > the
> > > > > calls into GeodeSecurityUtil/Shiro would act like non-existent. If
> > you
> > > > need
> > > > > special behavior of GeodeSecurityUtil, just init it with a property
> > > > object
> > > > > that would give you the behavior you want in your test like this,
> but
> > > > then
> > > > > we've had tests that already covers the security aspect of all the
> > > > calling
> > > > > code, so your test would not need to.
> > > > >
> > > > > @Test
> > > > > public void testInitialSecurityFlags() {
> > > > >  // initial state of GeodeSecurityUtil
> > > > >  assertFalse(GeodeSecurityUtil.isClientSecurityRequired());
> > > > >  assertFalse(GeodeSecurityUtil.isIntegratedSecurity());
> > > > >  assertFalse(GeodeSecurityUtil.isPeerSecurityRequired());
> > > > > }
> > > > >
> > > > > @Test
> > > > > public void testInitWithSecurityManager() {
> > > > >  properties.setProperty(SECURITY_MANAGER,
> > > > > "org.apache.geode.security.templates.SampleSecurityManager");
> > > > >  GeodeSecurityUtil.initSecurity(properties);
> > > > >  assertTrue(GeodeSecurityUtil.isClientSecurityRequired());
> > > > >  assertTrue(GeodeSecurityUtil.isIntegratedSecurity());
> > > > >  assertTrue(GeodeSecurityUtil.isPeerSecurityRequired());
> > > > > }
> > > > >
> > > > > @Test
> > > > > public void testInitWithClientAuthenticator()
> > > > > {
> > > > >  properties.setProperty(SECURITY_CLIENT_AUTHENTICATOR,
> > "org.abc.test");
> > > > >  GeodeSecurityUtil.initSecurity(properties);
> > > > >  assertTrue(GeodeSecurityUtil.isClientSecurityRequired());
> > > > >  assertFalse(GeodeSecurityUtil.isIntegratedSecurity());
> > > > >  assertFalse(GeodeSecurityUtil.isPeerSecurityRequired());
> > > > > }
> > > > >
> > > > > @Test
> > > > > public void testInitWithPeerAuthenticator()
> > > > > {
> > > > >  properties.setProperty(SECURITY_PEER_AUTHENTICATOR,
> "org.abc.test");
> > > > >  GeodeSecurityUtil.initSecurity(properties);
> > > > >  assertFalse(GeodeSecurityUtil.isClientSecurityRequired());
> > > > >  assertFalse(GeodeSecurityUtil.isIntegratedSecurity());
> > > > >  assertTrue(GeodeSecurityUtil.isPeerSecurityRequired());
> > > > > }
> > > > >
> > > > > @Test
> > > > > public void testInitWithShiroAuthenticator()
> > > > > {
> > > > >  properties.setProperty(SECURITY_SHIRO_INIT, "shiro.ini");
> > > > >  GeodeSecurityUtil.initSecurity(properties);
> > > > >  assertTrue(GeodeSecurityUtil.isClientSecurityRequired());
> > > > >  assertTrue(GeodeSecurityUtil.isIntegratedSecurity());
> > > > >  assertTrue(GeodeSecurityUtil.isPeerSecurityRequired());
> > > > > }
> > > > >
> > > > >
> > > > > On Wed, Jul 20, 2016 at 10:16 AM, Kirk Lund <kl...@pivotal.io
> > > > <javascript:;>> wrote:
> > > > >
> > > > >> To avoid adding boilerplate code, we could just avoid writing any
> > new
> > > > >> static methods or classes of static methods. Remember static is
> bad
> > in
> > > > OO
> > > > >> and is one of the main things that prevents true, good unit
> testing
> > > > because
> > > > >> you can't replace the class or the method with a fake of any sort
> > > (mock,
> > > > >> spy, or dummy).
> > > > >>
> > > > >> I would vote to eliminate all statics and adopt a policy of no new
> > > > statics
> > > > >> and follow:
> > > > >>
> > > > >> C.1) change all methods in GeodeUtils to be non-static and pass in
> > an
> > > > >> instance of GeodeUtils to the class constructor of any class that
> > will
> > > > use
> > > > >> it
> > > > >>
> > > > >> -Kirk
> > > > >>
> > > > >>
> > > > >> On Wed, Jul 20, 2016 at 9:57 AM, Jinmei Liao <jil...@pivotal.io
> > > > <javascript:;>> wrote:
> > > > >>
> > > > >>> One other to bear in mind is to avoid adding too much boiler code
> > > into
> > > > >> the
> > > > >>> production solely for the purpose of testing. I am typically a OO
> > > > person,
> > > > >>> but I wouldn't sacrifice code clarity and readability for it.
> > > > >>>
> > > > >>> On Fri, Jul 15, 2016 at 12:14 PM, Kirk Lund <kl...@pivotal.io
> > > > <javascript:;>> wrote:
> > > > >>>
> > > > >>>> We recently introduced GeodeSecurityUtil for GEODE-17, and I'm
> > > looking
> > > > >>> into
> > > > >>>> ways to refactor this class or the classes using it to better
> > > > >> facilitate
> > > > >>>> unit testing of those classes. I'm hoping that others on this
> list
> > > > will
> > > > >>>> chime in with ideas and opinions. GeodeSecurityUtil itself is a
> > > > >>> collection
> > > > >>>> of public static methods that use Apache Shiro and callback APIs
> > > (such
> > > > >> as
> > > > >>>> SecurityManager) in Geode.
> > > > >>>>
> > > > >>>> First, a few general pre-assertions:
> > > > >>>> A.1) we want to embrace clean coding and solid OOP coding
> > practices,
> > > > >>>> A.2) we want to write fast running unit tests that fully isolate
> > the
> > > > >>> class
> > > > >>>> under test (this typically uses mocking),
> > > > >>>> A.3) we want to be able to unit test any code in a class even if
> > > it's
> > > > >> in
> > > > >>> a
> > > > >>>> method that invokes public static methods in another class,
> > > > >>>> A.4) simple mocking with Mockito is much preferred over using
> > > > >> Powermock,
> > > > >>>> A.5) we want to also write integration tests but the emphasis is
> > on
> > > > >> fast
> > > > >>>> unit tests.
> > > > >>>>
> > > > >>>> There are quite a lot of procedurally static classes already in
> > the
> > > > >> Geode
> > > > >>>> codebase, so I'm hoping some of these ideas will be applicable
> to
> > > > >> helping
> > > > >>>> out others working on other areas of the code as well.
> > > > >>>>
> > > > >>>> Any class that has calls to a collaborator with public static
> > > methods
> > > > >>> makes
> > > > >>>> the unit testing of that class much more difficult:
> > > > >>>>
> > > > >>>> B.1) you cannot spy the static util class to verify that the
> class
> > > > >> under
> > > > >>>> test invoked it with proper parameters
> > > > >>>>
> > > > >>>> B.2) you cannot mock the static util class to return expected
> > values
> > > > to
> > > > >>> the
> > > > >>>> class under test
> > > > >>>>
> > > > >>>> If the following is a util type class:
> > > > >>>>
> > > > >>>> public class GeodeUtils {
> > > > >>>>
> > > > >>>>  public static void persist(String key) {
> > > > >>>>    ...
> > > > >>>>  }
> > > > >>>>
> > > > >>>>  public static String getValue() {
> > > > >>>>    ...
> > > > >>>> }
> > > > >>>> }
> > > > >>>>
> > > > >>>> ...then there are two potential ways to facilitate unit testing
> of
> > > > >>> classes
> > > > >>>> that depend on GeodeUtils:
> > > > >>>>
> > > > >>>> C.1) change all methods in GeodeUtils to be non-static and pass
> in
> > > an
> > > > >>>> instance of GeodeUtils to the class constructor of any class
> that
> > > will
> > > > >>> use
> > > > >>>> it
> > > > >>>>
> > > > >>>> C.2) wrap these statics with an interface and default
> > implementation
> > > > >> that
> > > > >>>> delegates to the static calls:
> > > > >>>>
> > > > >>>> public interface GeodeService {
> > > > >>>>
> > > > >>>>  void persist(String key);
> > > > >>>>
> > > > >>>>  String getValue();
> > > > >>>> }
> > > > >>>>
> > > > >>>> public class DefaultGeodeService implements GeodeService {
> > > > >>>>
> > > > >>>>  public void persist(String key) {
> > > > >>>>    GeodeUtils.persist(key);
> > > > >>>>  }
> > > > >>>>
> > > > >>>>  public String getValue() {
> > > > >>>>    return GeodeUtils.getValue();
> > > > >>>>  }
> > > > >>>> }
> > > > >>>>
> > > > >>>> public class DurableOperation {
> > > > >>>>
> > > > >>>>  private final Properties config;
> > > > >>>>  private final GeodeService geodeService;
> > > > >>>>
> > > > >>>>  public DurableOperation(Properties config) {
> > > > >>>>    this(config , new DefaultGeodeService());
> > > > >>>>  }
> > > > >>>>
> > > > >>>>  DurableOperation(Properties config, GeodeService geodeService)
> {
> > > > >>>>    this.config = config;
> > > > >>>>    this.geodeService = geodeService;
> > > > >>>>  }
> > > > >>>>
> > > > >>>>  public void perform(String key) throws IOException {
> > > > >>>>    ...
> > > > >>>>    this.geodeService.persist(key);
> > > > >>>>    ...
> > > > >>>>  }
> > > > >>>> }
> > > > >>>>
> > > > >>>> public class DurableOperationTest {
> > > > >>>>
> > > > >>>>  @Test
> > > > >>>>  public void shouldPersistKey() throws Exception {
> > > > >>>>    // given
> > > > >>>>    Properties config = generateFancyOperationConfig();
> > > > >>>>    GeodeService spyGeodeService = spy(GeodeService.class);
> > > > >>>>    when(spyGeodeService.getValue()).thenReturn("bar");
> > > > >>>>    DurableOperation durableOperation = new
> > DurableOperation(config,
> > > > >>>> spyGeodeService);
> > > > >>>>
> > > > >>>>    // when
> > > > >>>>    durableOperation.perform("foo");
> > > > >>>>
> > > > >>>>    // then
> > > > >>>>    verify(spyGeodeService, times(1)).persist("foo");
> > > > >>>>  }
> > > > >>>> }
> > > > >>>>
> > > > >>>> C.3) do C.2 until C.1 can occur (what prevents us from just
> doing
> > > > C.1?)
> > > > >>>>
> > > > >>>> I believe that in general we want to use the solution in C.1
> where
> > > > >>>> possible.
> > > > >>>>
> > > > >>>> To foster some discussion:
> > > > >>>>
> > > > >>>> D.1) do we agree on the pre-assertions I listed in A.1-5?
> > > > >>>> D.2) do we have procedurally static classes that cannot or
> should
> > > not
> > > > >> be
> > > > >>>> changed from static to non-static as described by C.1?
> > > > >>>> D.3) are there any other approaches that will better facilitate
> > unit
> > > > >>>> testing of classes that depend on static collaborators?
> > > > >>>>
> > > > >>>> I'd like to ultimately encourage the Geode dev community to
> avoid
> > > > >>> creating
> > > > >>>> new classes that consist of static methods and state. I believe
> we
> > > can
> > > > >> do
> > > > >>>> this and that it's for the best.
> > > > >>>>
> > > > >>>> -Kirk
> > > > >>>>
> > > > >>>
> > > > >>>
> > > > >>>
> > > > >>> --
> > > > >>> Cheers
> > > > >>>
> > > > >>> Jinmei
> > > > >>>
> > > > >>
> > > > >
> > > > >
> > > > >
> > > > > --
> > > > > Cheers
> > > > >
> > > > > Jinmei
> > > >
> > > >
> > >
> >
>
>
>
> --
> Cheers
>
> Jinmei
>

Reply via email to