Handling test data that depends on temporal data (that might change while the test runs)
Gabriel Genellina
gagsl-py2 at yahoo.com.ar
Thu May 15 20:54:37 EDT 2008
En Thu, 15 May 2008 14:21:34 -0300, Marcelo de Moraes Serpa
<celoserpa at gmail.com> escribió:
> So, I have this particular method, generate_chat_dir_string, which should
> generate a string in the following format:
> "md5hexstring-day-month-year-hour-minute"
> This string will be used to create a directory in the filesystem.
There is an issue here - how much behavior is considered in the public
interfase, and how much is just an implementation detail. You only have to
test the public interfase, what the method promises to do, NOT any
internal implementation details.
I don't think that the md5 string be part of the interfase - probably it's
enough to promise different directory names for different combinations of
"user" and "atendente". A separate question: do we promise to generate the
same directory name for the same set of user/atendente, or not? (assuming
the time part is constant).
For the time part - is that format *required*, or just a means to obtain
separate directory names, even for the same set of users, based on time?
Or perhaps we *only* want to guarantee different results for different
function calls, and ALL that stuff involving users and time are just
implementation details?
Depending on how you answer those questions, you write tests in a way or
another way.
> I'm trying to adopt the TDD approach, so, I'm starting by testing all the
> methods of the Chat class, including this one. However, an issue just
> popped
> up, this is the test code:
>
>
> 1. def test_generate_chat_dir_string(self):
> 2. import md5
> 3. import random
> 4. import datetime
> 5. md5_handler = md5.new()
> 6. users = [{"user":"pedro","atendente":False},{
> "user":"joão","atendente":True}]
> 7. #salt = random.random().to_s()
> 8. #md5_handler.update(salt)
> 9. now = datetime.datetime.now()
> 10. ts = now.strftime("%d-%m-%Y-%H-%M")
> 11. for user in users:
> 12. md5_handler.update(user["user"])
> 13.
> 14.
> 15. seed = md5_handler.hexdigest()
> 16.
> 17. final_string = seed + "-" + ts
> 18.
> 19. method_generated_string =
> self.chat.generated_chat_dir_string
> (users,seed)
> 20.
> 21. self.assertEquals(final_string,method_generated_string)
Looks like you have just copied the generated_chat_dir_string
implementation here - that's not the point of testing, at least if you
relax the restrictions as I said above.
Supose the specification says, instead: the resulting directory must
differ whether the set of users/atendente differs, or the request time is
separated more than 1 minute apart.
Then the tests should cover:
- build a set of users and choose a point in time
- generate dir1 with that set of users and that time
- modify the set of users, generate dir2 and assert that it differs from
dir1
Repeat other modifications, including:
one more user
one less user
same users, changing "atendente" for some
only one user
empty set of users (if it should raise an exception, check that it's
actually raised)
what happens when only the *order* of users is changed? (why is it a
list instead of a set?)
other conditions that might fail, perhaps based on your knowledge of
the implementation (it's ok to test special cases that are special only
because of how it is implemented, as far as you only test what is required
by the specification). By example, suppose the md5 function is absolutely
dumb and only accepts strings at most 32 characters long; test with a
username longer than that.
- Then, increment the time less than a minute and verify you *can*
generate dir3 (our specification doesn't *require* it to be the same, so
don't check more than required)
- increment the time more than a minute, generate another dir and verify
it's different.
Notice that, to be able to make all those tests involving the *same* time
we should process them very quickly so the minute part doesn't change (but
it even might fail when we're near the minute end). Worse, the last test
woul require to *wait* more than one minute to see the change...
That's horrible - so we write the function in a way that *helps* testing.
By example, adding another argument: the time to be used, which defaults
to None meaning the current time.
def generated_chat_dir_string(self, users, curtime=None):
if curtime is None: curtime = time.time()
...
(usually curtime is unused, but we do use it for testing)
I hope it's more clear now how to define your tests.
--
Gabriel Genellina
More information about the Python-list
mailing list