Archive for the ‘impersonators’ tag
Impersonators: Using them in showcases
Towards the end of my colleague Julio Maia's blog post about the impersonator pattern he suggests that the standalone environment that we can create through the use of impersonators can be quite useful for showcases and we actually had a recent occasion where we had to switch mid-showcase from using the integration environment to make use of an impersonator.
In this case part of the environment went down in the middle of the showcase so if we wanted to keep on going then that was our only option but in general the expectation of the business is that our showcases show them the functionality of the application end to end.
We need to be careful when using a standalone environment that the business are aware of this because it's quite likely that the application is going to respond much more quickly than it would if it was fully integrated.
If people aren't aware of this then they will be very surprised when the application isn't quite as performant when we put it into a real environment and it has to retrieve and store real data instead of just getting pre-stored values from an in memory impersonator.
Another disadvantage of using a standalone environment is that we must ensure that we only enter data which we have previously entered otherwise we'll either go against the real environment if we're using a self initializing fake or (I think) just get no response if we're using a record/replay type impersonator.
If we're letting a business user drive the application during a showcase then we'll need to ensure that they know which data they can enter.
As long as we're careful in those regards I think it can be quite a useful approach since we can make our showcase independent of factors that our outside of our control – for example in our most recent showcase we had to reschedule the time because one part of the server was going to be restarted at our initial showcase time.
I definitely like the idea of using the impersonator for the safety it brings although my thinking at the moment is that maybe it's best to use it as a backup option if the real environments aren't working.
I'd be interested to hear if others have experiences around this area.
Impersonators: Finding the enabling point
One of the other interesting problems that we've come across while making use of different impersonators in our build process, and which Julio mentions at the end of his comment on Gil Zilberfeld's blog post, is trying to work out where the correct place for the impersonator is.
Ideally we want to put the impersonator in a place where we can easily turn it on or off depending on whether we want to use the impersonator or the real end point. In fact if we aren't able to do this then it is perhaps the case that we haven't actually created an impersonator at all.
I like to use the term 'enabling point', which I learnt from Michael Feathers in his chapter on seams in Working Effectively With Legacy Code, to describe the place where we can make the decision to use the impersonator or the real end point.
Ideally this will probably be determined by changing a simple setting in a configuration file.
If you choose the wrong enabling point the 'impersonator' may not actually help you that much and it might even become such a hindrance that you stop using it and just accept the problems we can have when we have to integrate against real end points all the way through our build process.
On the way to making use of the self initializing fake on my current project we tried/thought about some other approaches which we thought might be able to ease the pain of integrating directly against the service layer which were quite unstable at the time.
We were often losing 4 or 5 hours a day due to the website being unusable without this integration point working.
Stubs
The first thought was that perhaps we could just integrate against a stub of each of the services in our development environment and then inject either the stub or real service into our code through dependency injection.
Julio points out quite an important flaw with this approach:
It happens that the results of those tests have very limited value, as they're not actually validating if the system actually integrates correctly with its environment.
If there was a change to the structure of the xml being returned we wouldn't know about this until we actually ran a test against the real integration point and our feedback cycle is now fairly slow.
In addition we would be returning a canned set of results from data that we had setup and the maintenance of this canned data just becomes a real pain after a while.
The internal cache
The first approach that we tried involved intercepting the requests/responses of each of the service calls from inside the application itself by making use of interceptors injected from our DI container around the classes which made the calls.
We eventually managed to get this working to a stage where we could save the requests and responses to disk and then make use of this data when the services weren't working.
The problem with this approach was that we were configuring it by configuring a 'UseCache' property in our configuration file which I think was probably a sign that we were doing something wrong, the problem being that we made use of the 'UseCache' property in our production code.
We only setup the recording part of the process to record the requests and responses from our service tests when run as part of the build and then saved those results onto a remote server.
Another step we needed to do to make use of the 'impersonator' was therefore to copy those files across to the local machine and put them into the 'cache directory' which was another configurable property.
Although it only took maybe 5 minutes to do this it became quite annoying after a while. If we have found a good enabling point then it should be really easy to switch to using it which wasn't the case here.
In addition we know had all this impersonating code inside our main solution and it became more and more complex as we tried to make it more useful – another lesson here was that if we're going to write an impersonator, that code should be outside of our main application.
Self Initializing Fake
The self initializing fake is our current approach and the nice thing about this approach is that it's really easy to switch back and forth between this and the real end point – all you need to do is change the 'ServiceUrl' value in the configuration file!
The self initializing fake is a recording proxy which sits on our CI server and captures the requests and responses to/from the service and then stores those results in memory.
If you make the same request again it returns the response from its store instead of sending that request through to the service layer.
The code for the self initializing fake is outside of our main solution – in fact it's actually written in Ruby and the application is in C#.
In summary
From my experience it's quite important to make use of impersonators of our integration points if we are to get a stable environment to run against but we can cause ourselves a lot of pain if we pick the wrong enabling point for that impersonator.
The key seems to be that it should be minimal effort to enable an impersonator and we shouldn't need to make any changes to our production code in order to do so.
From working with some other impersonators I think it is also important that we shouldn't have to make any changes to our test code in order to use an impersonator either.
If we find ourselves having to do something that seems crazy with impersonators it might well be worth considering whether we have the right enabling point.
Impersonators: Why do we need them?
I wrote previously about an impersonator we are using on my project which Martin Fowler has dubbed the 'self initializing fake' and although I thought this was the only type of situation where we might use this approach, from discussing this with my colleague Julio Maia and from experiences on the project I'm working on I realise there are other advantages to this approach as well.
To deal with unstable/slow integration points
This is the main reason that we use the self initializing fake and provides perhaps the most obvious reason why we might create an impersonator because we will remain in pain if we don't create one.
Before we used it all our acceptance tests went from the UI all the way through to the back end via a service layer which often led to tests failing due to timeouts (the service took longer than expected to respond) or problems somewhere in the back end.
These 'random build failures' were amazingly frustrating since it meant that maybe more than 50% of the time that our continuous integration build light was red it wasn't actually related to anything that the person who checked in had done.
The confidence in the build light had drained and 'red builds' were pretty much being ignored.
To speed up the feedback cycle
A nice side effect that we got from putting in an impersonator is that our tests run about 5x more quickly than they did previously due to the fact that the impersonator is on a machine in Sydney whereas the actual end point is on a machine in Melbourne and because the impersonator just returns the same response for a given request each time instead of having to recalculate each time.
It is quite common on projects to use an in memory database instead of the real database for integration tests to allow these tests to run more quickly and I think this logic works well when applied to other systems we need to integrate against as well.
In general if we can have as much of the system as possible under our control in the early parts of our build pipeline then we can have a continuous integration build which gives us very quick feedback and only fails due to problems we created.
To handle not yet ready integration points early
Although we would rather do integration early on in a project so that we can discover any problems and then fix them, sometimes the way the project has been planned means that a system that we need to integrate won't actually be ready until much later on.
An example of this which I've seen a couple of times is integrating applications with single sign on systems.
Even though we don't know exactly how we will integrate against the real system we might be able to take a reasonably good guess of how it might work e.g. our application is able to detect whether a user is logged in based on some information set in the headers of each request by an ISAPI filter which each request passes through before reaching our application.
On this occasion we didn't developer an impersonator for this integration point until after we already had the real end point but I think it would probably have made life easier if we had.
Impersonators and Continuous Integration
I'm sure there are more reasons why impersonators make sense but these are the ones that we've noticed so far.
The key with all these ways of impersonating an integration point is that they should only form part of our continuous integration pipeline -we do still need to test that our code works in a proper end to end test as well.
In our case we have another stage of our Cruise pipeline which runs all the tests against the real end points.
I'm curious as to what our pipeline should look like when we have built multiple impersonators for different end points – my current thinking is that we can have an early stage of the pipeline where we make use of all of the impersonators and then another stage straight after that where we use all the real end points.
However, it does seem like a bit of a jump to go from all impersonators to no impersonators and it increases the likelihood that the build is going to fail since we are going straight from minimal integration to having everything integrated.
I guess another approach would be to gradually introduce a real integration point on each part of the pipeline until we have everything integrated on the last one although this might actually result in our feedback being slower if its the last integration point which gives us problem.
Julio has written more about the impersonator pattern on his blog.