Our web application uses Stormpath and in some unit tests we sometimes pull in the complete Stormpath client side stack in order to test some behavior or other.
But we don't want the Stormpath stack making remote calls. Achieving this isn't too complicated - we replace the usual DefaultClient with one where we've mocked the InternalDataStore so that it doesn't make remote connections.
I've put together a mini-project to demonstrate that this works well - mocked-data-store-client. I'll go into that in a minute.
This works - but our problem is that we're clearly mocking elements of the Stormpath stack that aren't really meant to be manipulated by the SDK's end users.
In particular the signature of the method DefaultClient.createDataStore, which we override to plugin our mocked data store, has changed every few releases - it changed with 1.1.1 and I can see in Github that it will change again for the next version (the recent changes were by iancorcoran).
So the question is how should we be doing things when we do want to pull in the Stormpath stack for a unit test but don't want it making remote requests?
OK - let's go through my mini-project so you can see how we're doing things at the moment.
It's a super simple app with one REST controller DemoRestController with one method that handles requests for the path /rest/email-address. If the user is logged in it will return the email address stored in Stormpath for the current user, not logged in users will get a 401.
The app comes with a very basic MyappSecurityConfig that calls StormpathWebSecurityConfigurer.stormpath().
Apart from additional utility code, to makes sure unauthorized REST calls result in a 401, rather than a 302 redirect to the Stormpath login page, that's it for src/main part of things.
Then under test we have a test-application.properties where stormpath.application.id and stormpath.application.href are set (to bogus values).
Then we have a very normal looking test DemoRestControllerTest that uses MockMvc etc. to call our /rest/email-address logic. First as an unauthorized user to confirm that we get a 401 response. And then as an authorized user to confirm that we get back an email address.
If you just clone the repo and run mvn test you should see this test run through fine.
So far nothing too surprising - but now the magic that this question is all about. The @SpringBootTest annotation on our test pulls in our StormpathTestConfig. This class is itself very simple:
It just uses @EnableAutoConfiguration to pull in the kitchen sink, i.e. scan all our dependencies and pull in things like Stormpath.
It provides two @Bean annotated methods to replace standard Stormpath beans in order to achieve our goal of a Stormpath setup that makes no remote requests.
The second of these two beans is the interesting one - MockDataStoreClient - this is a DefaultClient subclass that creates and uses a mocked InternalDataStore.
As remarked above it works fine, but it seems too internal to the SDK to be something end users of the SDK, like ourselves, should be messing with. It's the overridden createDataStore method in this class that breaks often when we upgrade our Stormpath dependency version.
MockedSecurity is the only remaining class - we use it in out simple DemoRestControllerTest to mock up just enough stuff that we can get through StormpathAuthenticationProvider.authenticate etc. and be seen as being authenticated when making a REST call from our test.
Our web application uses Stormpath and in some unit tests we sometimes pull in the complete Stormpath client side stack in order to test some behavior or other.
But we don't want the Stormpath stack making remote calls. Achieving this isn't too complicated - we replace the usual
DefaultClient
with one where we've mocked theInternalDataStore
so that it doesn't make remote connections.I've put together a mini-project to demonstrate that this works well - mocked-data-store-client. I'll go into that in a minute.
This works - but our problem is that we're clearly mocking elements of the Stormpath stack that aren't really meant to be manipulated by the SDK's end users.
In particular the signature of the method
DefaultClient.createDataStore
, which we override to plugin our mocked data store, has changed every few releases - it changed with 1.1.1 and I can see in Github that it will change again for the next version (the recent changes were by iancorcoran).So the question is how should we be doing things when we do want to pull in the Stormpath stack for a unit test but don't want it making remote requests?
OK - let's go through my mini-project so you can see how we're doing things at the moment.
It's a super simple app with one REST controller
DemoRestController
with one method that handles requests for the path/rest/email-address
. If the user is logged in it will return the email address stored in Stormpath for the current user, not logged in users will get a 401.The app comes with a very basic
MyappSecurityConfig
that callsStormpathWebSecurityConfigurer.stormpath()
.Apart from additional utility code, to makes sure unauthorized REST calls result in a 401, rather than a 302 redirect to the Stormpath login page, that's it for
src/main
part of things.Then under test we have a
test-application.properties
wherestormpath.application.id
andstormpath.application.href
are set (to bogus values).Then we have a very normal looking test
DemoRestControllerTest
that usesMockMvc
etc. to call our/rest/email-address
logic. First as an unauthorized user to confirm that we get a 401 response. And then as an authorized user to confirm that we get back an email address.If you just clone the repo and run
mvn test
you should see this test run through fine.So far nothing too surprising - but now the magic that this question is all about. The
@SpringBootTest
annotation on our test pulls in ourStormpathTestConfig
. This class is itself very simple:@EnableAutoConfiguration
to pull in the kitchen sink, i.e. scan all our dependencies and pull in things like Stormpath.@Bean
annotated methods to replace standard Stormpath beans in order to achieve our goal of a Stormpath setup that makes no remote requests.The second of these two beans is the interesting one -
MockDataStoreClient
- this is aDefaultClient
subclass that creates and uses a mockedInternalDataStore
.As remarked above it works fine, but it seems too internal to the SDK to be something end users of the SDK, like ourselves, should be messing with. It's the overridden
createDataStore
method in this class that breaks often when we upgrade our Stormpath dependency version.MockedSecurity
is the only remaining class - we use it in out simpleDemoRestControllerTest
to mock up just enough stuff that we can get throughStormpathAuthenticationProvider.authenticate
etc. and be seen as being authenticated when making a REST call from our test.