Unit Testing: a couple of questions

E

Emanuele D'Arrigo

Hi everybody,

I'm just having a go with Unit Testing for the first time and my
feeling about it in short is: Neat!

I'm a bit worried about the time it's taking me to develop the tests
but after only a day or so I'm already much faster than when I started
with it and the code is already much improved in terms of robustness.
A couple of "philosophical" questions have emerged in the process.

1) Granularity
Given a simple class

class myClass():
def __init__(self, data):
__data = data;

def getData(self):
return __data

def setData(self, data):
__data = data

I've been wondering: where do I stop in terms of testing for things
that could go wrong? In this case for example, it might be reasonable
to expand the class to make sure it only receives integers and test
accordingly, i.e.:

def setData(self, data):
try:
data = int(data)
except ValueError:
raise ValueError("Argument received cannot be converted to
integer: " + data)

But would it be reasonable to test also for the assignment operators?
After all, if, for some strange reason, there isn't enough memory,
couldn't even __data = data potentially fail?

2) Testing in isolation
I'm not entirely clear on this point. I can see how I need to test
each path of the program flow separately. But should a test -only-
rely on the object being tested and mock ones in supporting roles?
I.e. would this be wrong if SupportObject is not a mockup?

def testObjectToBeTested _forReallyBadError(self):
supportObject = SupportObject()
objectToBeTested = ObjectToBeTested()
result = objectToBeTested.addSupportObject(supportObject)
self.failIf(result != kSuccess, "Support Object could not be
added!")

I can see how if the SupportObject class had a bug introduced in it,
this test would fail even though it has nothing to do with the
ObjectToBeTested class. However, creating mock objects can be quite an
overhead (?). I'm wondering if there is a threshold, even a fuzzy one,
under which it isn't worth doing and a test like the one above is
"good enough".

What do you guys think?

Manu
 
A

Antoine De Groote

I'm wondering if don't want your class to look something like this:

class myClass():
def __init__(self, data):
self.__data = data

def getData(self):
return self.__data

def setData(self, data):
self.__data = data

For the rest I'll let the experts argue, I don't have a lot of
experience with unit testing either... Shame on me! ;-)

Regards,
antoine
 
O

Orestis Markou

For the first bit, a colleague has recently asked the philosophical
question, "How do you test what happens when the power goes down?" :)

In other words, only test the bits that your code does. If you want to
provide type checking, then yes, you have to test that.

It's fair to assume that everything that is *not* your code will work
as expected. If you do find 3rd-party bugs that affect you, then you
should write a unit test that exposes that bug. When they fix it, your
unittest fails, prompting you to remove any workarounds you had.
 
M

Mel

Emanuele said:
I'm a bit worried about the time it's taking me to develop the tests
but after only a day or so I'm already much faster than when I started
with it and the code is already much improved in terms of robustness.
A couple of "philosophical" questions have emerged in the process. [ ... ]
But would it be reasonable to test also for the assignment operators?
After all, if, for some strange reason, there isn't enough memory,
couldn't even __data = data potentially fail?

I would say, don't test for anything you can't fix. If you have (what the
agile-programming people call) a "user story" describing how your program
will recover from out-of-memory, then test whether that recovery is carried
out. Otherwise forget it.

Best think of unit tests as your sub-program spec written in a different
form: i.e. as executable programs. You don't test for "things that can go
wrong", you test for behaviour that your program must exhibit.
2) Testing in isolation
I'm not entirely clear on this point. I can see how I need to test
each path of the program flow separately. But should a test -only-
rely on the object being tested and mock ones in supporting roles?
I.e. would this be wrong if SupportObject is not a mockup?

def testObjectToBeTested _forReallyBadError(self):
supportObject = SupportObject()
objectToBeTested = ObjectToBeTested()
result = objectToBeTested.addSupportObject(supportObject)
self.failIf(result != kSuccess, "Support Object could not be
added!")

I can see how if the SupportObject class had a bug introduced in it,
this test would fail even though it has nothing to do with the
ObjectToBeTested class. However, creating mock objects can be quite an
overhead (?). I'm wondering if there is a threshold, even a fuzzy one,
under which it isn't worth doing and a test like the one above is
"good enough".

I have heard people talk, matter-of-factly about support objects that got so
complicated that they needed unit tests of their own. Of course, anything
can be done in a ridiculous way, but good comprehensive unit tests give you
the eternal assurance that your program is behaving properly, through all
maintenance and modification. That's worth a lot.
 
M

Marco Bizzarri

Hi everybody,

I'm just having a go with Unit Testing for the first time and my
feeling about it in short is: Neat!

I'm a bit worried about the time it's taking me to develop the tests
but after only a day or so I'm already much faster than when I started
with it and the code is already much improved in terms of robustness.
A couple of "philosophical" questions have emerged in the process.

1) Granularity
Given a simple class

class myClass():
def __init__(self, data):
__data = data;

def getData(self):
return __data

def setData(self, data):
__data = data

I've been wondering: where do I stop in terms of testing for things
that could go wrong? In this case for example, it might be reasonable
to expand the class to make sure it only receives integers and test
accordingly, i.e.:

def setData(self, data):
try:
data = int(data)
except ValueError:
raise ValueError("Argument received cannot be converted to
integer: " + data)

But would it be reasonable to test also for the assignment operators?
After all, if, for some strange reason, there isn't enough memory,
couldn't even __data = data potentially fail?

2) Testing in isolation
I'm not entirely clear on this point. I can see how I need to test
each path of the program flow separately. But should a test -only-
rely on the object being tested and mock ones in supporting roles?


IMHO, don't do that. Mocking everything and anything can become
incredibly complex. Wait to do it. As usual, experience plays a big
role on this; start mocking on what will make your test *simpler* and
easier to write and to understand, for you today and for you tomorrow
(and remember that you tomorrow is a different one from you today).

Before writing a mock, ask yourself: can I use a real object? The both
of you know the behaviour of the real object; you hadn't to think
about what some you yesterday wrote inside it.

I.e. would this be wrong if SupportObject is not a mockup?

def testObjectToBeTested _forReallyBadError(self):
supportObject = SupportObject()
objectToBeTested = ObjectToBeTested()
result = objectToBeTested.addSupportObject(supportObject)
self.failIf(result != kSuccess, "Support Object could not be
added!")
I can see how if the SupportObject class had a bug introduced in it,
this test would fail even though it has nothing to do with the
ObjectToBeTested class.
However, creating mock objects can be quite an
overhead (?). I'm wondering if there is a threshold, even a fuzzy one,
under which it isn't worth doing and a test like the one above is
"good enough".


What do you guys think?

Manu

Creating mock objects in python can be quite easy; even more if you're
on a greenfield project, and not with some thousands or ten of
thousands of legacy code around. So, the burden is not that much. If
you end using more than a couple of mocks in one test, most probably
there is something wrong, and it would be better if you reconsider
your design.

Also, think if you really need mock object in your tests. Can't it be
easier to use a "real" object? Unless creating it is really complex,
and making it show the behaviour you want it to show is hard or
unclear, you can use it directly.

You wont' use a mock for a datetime object, am I right?

Moreover, don't fight your problem (integrating your objects) at the
wrong level (unit testing); you'll need integration tests, where you
use your *REAL* object end-to-end, maybe even talking with other
subsystems, in order to check that you are working for real. And
there,maybe, you could discover that your discipline failed and you
didn't changed the signature of the method of a mock object after you
changed it in the real one. It happens, so, put your tests in place to
catch this problem.

Regards
Marco
 
E

Emanuele D'Arrigo

Thank you all for the very instructive replies! Much appreciated!

By the sound of it I just have to relax a little and acquire a little
bit more experience on the matter as I go along. =)

Thank you again!

Manu
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top