Saturday, October 24, 2009

EXAMPLE









































Prev don't be afraid of buying books Next






























EXAMPLE



We must now write our first failing test before
we can code.





Test 1: An empty list should have a
size of zero




import unittest
from movie.Movie import *

class TestMovieList(unittest.TestCase):

def testZeroLengthMovieList(self):
movieList = MovieList()
self.assertEquals(movieList.getSize(),0)

if__name == '__main__':
unittest.main()






The test fails with:




===============================================
ERROR: testZeroLengthMovieList (__main__.TestMovieList)
----------------------------------------------------------------------
Traceback (most recent call last):
File "c:\python22\lib\movie\TestMovie.py", line 7, in testZeroLengthMovieList
movieList = MovieList()
NameError: global name 'MovieList' is not defined
----------------------------------------------------------------------
Ran 1 tests in 0.000s

FAILED (errors=1)






Now we will do the simplest thing that will give
us a passing test. We will stub out size in the movie
object to return zero.




class MovieList:

def getSize(self):
return 0






Green bar—the test passes with:

----------------------------------------------------------------------
Ran 1 tests in 0.000s

OK








Test 2: Adding a movie to an empty
list should result in a list with a size of one



It is now our responsibility to expand and test
for other behaviors of the MovieList object. We have also
refactored the test to remove redundancy of creating an instance of
MovieList and cleaning up the setUp() and
tearDown() methods of the test object.




class TestMovieList(unittest.TestCase):

def setUp(self):
self.movieList = MovieList()

def testZeroLengthMovieList(self):
self.assertEquals(self.movieList.getSize(),0)

def testAddMovie(self):
self.movieList.addMovie(Movie('Star Wars'))
self.assertEquals(self.movieList.getSize(),1)

def tearDown(self):
self.movieList=None






Red bar—the test fails with:




=========================================
ERROR: testAddMovie (__main__.TestMovieList)
----------------------------------------------------------------------
Traceback (most recent call last):
File "c:\python22\lib\movie\TestMovie.py", line 11, in testAddMovie
self.movieList.addMovie(Movie('Star Wars'))
AttributeError: MovieList instance has no attribute 'addMovie'
----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (errors=1)






Stub out the addMovie(movie) method
with a pass instruction. This method returns None when it is called
and does nothing.




class MovieList:

def getSize(self):
return 0

def addMovie(self, movie):
pass







Red bar
Movie object is not defined; we must stub out a
Movie class. Before we do this we need to add a
testMovie class to test the behavior of a Movie
and stub out Movie. This class only needs to retain its
name for now, so that is the only behavior we will test for.



Add test class:




class testMovie(unittest.TestCase):

def testMovie(self):
movie=Movie('Star Wars')
self.assertEquals(movie.name, 'Star Wars')






Create the code for the Movie class, a simple
class with only a constructor method.




class Movie:
def __init__(self, name=None):
self.name = name







Red bar
MovieList is still returning 0 for all
getSize() requests:




===========================================
FAIL: testAddMovie (__main__.TestMovieList)
----------------------------------------------------------------------
Traceback (most recent call last):
File "c:\python22\lib\movie\TestMovie.py", line 12, in testAddMovie
self.assertEquals(self.movieList.getSize(),1)
File "C:\Python22\Lib\unittest.py", line 273, in failUnlessEqual
raise self.failureException, (msg or '%s != %s' % (first, second))
AssertionError: 0 != 1
----------------------------------------------------------------------
Ran 3 tests in 0.000s

FAILED (failures=1)






Modify MovieList to track the size.
Yes, we know this is a stupid way to do it but we also know that
first we will make it work and then we will make it elegant.




class MovieList:

def __init__(self):
self.size=0

def getSize(self):
return self.size

def addMovie(self, movie):
self.size += 1







Green
bar
— All three of our tests are running at 100 percent.
We could now integrate our code with the main codebase depending on
our check-in strategy. Now let's add tests for the functionality to
see if a movie has already been added to the
MovieList.





Test 3: If we add a movie to a list,
we should be able to ask if it's there and receive a positive
response




class TestMovieList(unittest.TestCase):

def setUp(self):
self.movieList = MovieList()

def testZeroLengthMovieList(self):
self.assertEquals(self.movieList.getSize(),0)

def testAddMovie(self):
self.movieList.addMovie(Movie('Star Wars'))
self.assertEquals(self.movieList.getSize(),1)

def testContainsMovie(self):
self.movieList.addMovie(Movie('Star Wars'))
self.failUnless(self.movieList.containsMovie('Star Wars'))

def tearDown(self):
self.movieList=None







Red bar
Stub out the functionality in MovieList by simply
returning True when the containsMovie(name)
method is called.




class MovieList:

def __init __(self):
self.size=0

def getSize(self):
return self.size

def addMovie(self, movie):
self.size += 1

def containsMovie(self, name):
return True







Green
bar
— All four of our tests pass. Refactor test objects
to be a bit more modular. Notice that we pulled the test for zero
length list into another test object and created and then added
"Star Wars" to the list of movies. The methods setUp() and
teardown() are called for each of the test method
invocations in that class so we do not have to worry about test
crosstalk in the object.




class TestZeroLengthMovieList(unittest.TestCase):

def testZeroLengthMovieList(self):
movieList=MovieList()
self.assertEquals(movieList.getSize(),0)

class TestMovieList(unittest.TestCase):

def setUp(self):
self.movieList = MovieList()
self.movieList.addMovie(Movie('Star Wars'))

def testOneMovieList(self):
self.assertEquals(self.movieList.getSize(),1)

def testContainsMovie(self):
self.failUnless(self.movieList.containsMovie('Star Wars'))

def tearDown(self):
self.movieList=None







Green
bar
— Now we must extend the test to express the true
intent of the contains- Movie(name) method by testing to
ensure that we get a false if the movie has not been added.





Test 4: Asking about the presence of a
movie that wasn't added should result in a negative response




class TestMovieList(unittest.TestCase):

def setUp(self):
self.movieList = MovieList()
self.movieList.addMovie(Movie('Star Wars'))

def testOneMovieList(self):
self.assertEquals(self.movieList.getSize(),1)

def testContainsMovie(self):
self.failUnless(self.movieList.containsMovie('Star Wars'))

def testNotContainsMovie(self):
self.failIf(self.movieList.containsMovie('Star Trek'))

def tearDown(self):
self.movieList=None







Red bar
Extend MovieList to check if a movie has been added:




class MovieList:

def __init__(self):
self.size=0
self.movies=[]

def getSize(self):
return self.size

def addMovie(self, movie):
self.size += 1
self.movies.append(movie)

def containsMovie(self, name):
for movie in self.movies:
if movie.name == name:
return True
return False







Green
bar
— All tests pass. We are done. Or are we? We still
must refactor to make the implementation of our objects as clear as
possible and remove any redundancy. Our MovieList class is
using a counting scheme rather than checking the length of our
internal movie list.




class MovieList:

def __init__(self):
self.movies=[]

def getSize(self):
return len(self.movies)

def addMovie(self, movie):
self.movies.append(movie)

def containsMovie(self, name):
for movie in self.movies:
if movie.name == name:
return True
return False







Green
bar
— Done. Let's be a bit more thorough and expand our
tests to check if multiple movies in the list bother us.




class TestMovieList(unittest.TestCase):

def setUp(self):
self.movieList = MovieList()
self.movieList.addMovie(Movie('Star Wars'))

def testOneMovieList(self):
self.assertEquals(self.movieList.getSize(),1)

def testContainsMovie(self):
self.failUnless(self.movieList.containsMovie('Star Wars'))

def testNotContainsMovie(self):
self.failIf(self.movieList.containsMovie('Star Trek'))

def testMultipleMoviesInList(self):
self.assertEquals(self.movieList.getSize(),1)
self.movieList.addMovie(Movie('Un Chien Andalou'))
self.assertEquals(self.movieList.getSize(),2)
self.movieList.addMovie(Movie('Run Lola Run'))
self.assertEquals(self.movieList.getSize(),3)
self.failUnless(self.movieList.containsMovie('Un Chien Andalou'))
self.failIf(self.movieList.containsMovie('Star Trek'))

def tearDown(self):
self.movieList=None







Green
bar
— This last test is somewhat like an acceptance test
since it tests multiple scenarios. Many people would argue against
this level of redundancy, but for my money I believe that tests
should be fragile and that some level of acceptance test should be
expressed while unit-testing the application. The redundant asserts
could certainly be factored out into private methods if one
wishes.















































Amazon






No comments:

Post a Comment