You really want your tests to be fast and consistent. If you start creating tests that may occasionally fail due to eventual consistency, you'll ignore the test when it fails, and then what use is it?
Create a fake service which handles the PUT and GET requests, but has an additional operation to make it consistent.
class TestDataStore:
def __init__(self):
self.current = None
self.pending = self.current
def do_put(self, obj):
self.pending = obj
def make_consistent(self):
self.current = self.pending
def do_get(self):
return self.current
Your test is then:
datastore.do_put(myobj);
datastore.make_consistent();
validate(datastore.do_get(), myobj);
This allows you to test your software's behavior when the GET successfully retrieves the PUT object. It also allows you to test your software's behavior when the GET does not find the object (or the correct object) due to the service not yet being consistent. Just leave out the call to make_consistent()
.
It is still worth having tests that interact with the real service, but they should run outside your normal development workflow, as they will never be 100% reliable (e.g. if the service is down). These tests should be used to:
- provide metrics on average and worst case time between a PUT and a subsequent GET becoming consistent; and
- verify that your fake service behaves similarly to the real service. See https://codewithoutrules.com/2016/07/31/verified-fakes/
How can I reproducibly test an eventually consistent service?
-- You can't. You have to remove the word "reproducibly" or the word "eventually;" you can't have both.