Caching Answers
This guide shows how to automatically cache answers to Questions. Answer caching is an optional advanced feature of Boa Constrictor.
When Should Actors Cache Answers?
Sometimes, an Actor needs to call a Question repeatedly to get the same answer. For example, multiple tests in a suite might need to query the same product configurations. Caching the answer on the first call and returning it on subsequent calls would be much more efficient than repeatedly asking the same Question. Boa Constrictor’s support for answer caching spares programmers from implementing their own mechanisms for storing and sharing answers.
Answers should be cached when they are expected to be immutable, or unchanging. For example, product configurations may be the same for the duration of a test suite.
However, answers should not be cached when they represent “fresh” information or they could change when called again. For example, a web element’s scraped text may not be appropriate to cache because it represents the state of the page at a moment in time.
Writing Cacheable Questions
Not all Questions are inherently cacheable.
Questions must implement the ICacheableQuestion<TAnswer>
interface,
where TAnswer
is the answer type:
namespace Boa.Constrictor.Screenplay
{
public interface ICacheableQuestion<TAnswer> : IQuestion<TAnswer>
{
bool Equals(object obj);
int GetHashCode();
}
}
Cacheable Questions must implement the standard Equals
and GetHashCode
methods.
These should be implemented the same way as they would be for any other C# class.
Typically, all properties and instance variables for a Question should be included
in the implementations of Equals
and GetHashCode
methods.
For example, here is how to implement them for a Question that gets a browser cookie by name:
using Boa.Constrictor.Screenplay;
public class BrowserCookie : ICacheableQuestion<TAnswer>
{
private string CookieName { get; set; }
// ... other code ...
public override bool Equals(object obj) =>
obj is BrowserCookie cookie &&
CookieName == cookie.CookieName;
public override int GetHashCode() =>
HashCode.Combine(GetType(), CookieName);
}
The Equals
method must return true only for equivalent Questions.
The GetHashCode
method must return a unique hash code based on the Question’s properties and variables.
Otherwise, answer caching might have collisions, which could return incorrect answers for Questions.
All WebDriver Questions implement ICacheableQuestion<TAnswer>
.
Enabling Actors to Cache Answers
Actors use the CacheAnswers
Ability to store answers from cacheable Questions in an AnswerCache
object.
AnswerCache internally stores key/value pairs in which a Question is a key and its answer is the value.
(This is why cacheable Questions must implement Equals
and GetHashCode
.)
Set up the Ability like this:
using Boa.Constrictor.Screenplay;
var actor = new Actor();
var cache = new AnswerCache();
actor.Can(CacheAnswers.With(cache));
The AnswerCache
object is thread-safe.
Answers are added and cleared synchronously.
Caching Answers When Asking Questions
An Actor uses the AsksFor
method to ask Questions.
However, in order to automatically store the answer in the AnswerCache
object,
the Actor should instead use the GetsCached
method, like this:
var cookie = actor.GetsCached(BrowserCookie.FromBrowser());
Whenever the Actor wants to use answer caching,
it will check if its AnswerCache
object already has an answer for the given Question.
If the AnswerCache
has an answer, then the Actor will return it without calling the Question.
Otherwise, the Actor will call the Question and then add the answer to its AnswerCache
.
Any time the Actor asks the same Question again, they will get the stored response.
This is very useful in large test suites when multiple tests ask the same Questions.
The Discovers
method is an alias for GetCached
and works the same way.
The following call is equivalent to the one above:
var cookie = actor.Discovers(BrowserCookie.FromBrowser());
The GetsCached
and Discovers
methods are
Actor extension methods.
Internally, they use a special Question named CachedAnswer<TAnswer>
.
Their calls are equivalent to the following:
var cookie = actor.AsksFor(CachedAnswer<TAnswer>.For(BrowserCookie.FromBrowser()));
The extension methods make caching calls much more concise.
Bypassing the Answer Cache
Actors can call cacheable Questions without using the answer cache via the AsksFor
method:
var cookie = actor.AsksFor(BrowserCookie.FromBrowser());
In this case, the Actor will bypass the answer cache and call the Question as if it were any ordinary Question. It will neither fetch nor store answers using the cache.
Remember to use GetsCached
or Discovers
instead of AsksFor
(or AskingFor
) when you want to cache answers.
It is a common mistake to call AsksFor
when answer caching is desired.
Clearing the Answer Cache
Sometimes, answers stored in the cache should be reset.
To clear the answer for a specific Question, call the Invalidate
method with the Question:
cache.Invalidate(BrowserCookie.FromBrowser());
To clear all answers from the cache, call the InvalidateAll
method:
cache.InvalidateAll();