Quantcast
Channel: Planet Plone - Where Developers And Integrators Write
Viewing all articles
Browse latest Browse all 3535

4teamwork: Construct your Testing Data using the Builder Pattern

$
0
0

Test data generation is always a hot topic when it comes to automated software testing. One of my first assignments at 4teamwork was to help with the testing. In the process I was browsing through the test suites of some big projects they are working on and noticed a lot of similar code related to data creation:

12345678910111213
dossier=createContentInContainer(self.portal,'opengever.dossier',title='Dossier',checkConstraints=False)test_file=NamedBlobFile("lorem ipsum",filename=u"test.txt")document=createContentInContainer(dossier,'opengever.document',checkConstraints=False,title=u'Foobar',keywords=[],file=test_file)transaction.commit()

This is very basic object creation code and I’m sure many Plone developers have written a fair amount of it. While this gets the job done, there is a lot of noise, that we don’t need to have in every test.

I decided to use a basic implementation of the Builder Pattern to hide the noise in specific Builder classes and make our tests shorter and more expressive at the same time:

123456789101112131415161718192021222324252627282930313233343536373839404142
classDossierBuilder(object):def__init__(self,session):self.arguments={"checkConstraints":False}deftitled(self,title):self.arguments["title"]=titlereturnselfdefwithin(self,container):self.container=containerreturnselfdefcreate(self):obj=createContentInContainer(self.container,'opengever.dossier',**self.arguments)transaction.commit()returnobjclassDocumentBuilder(object):def__init__(self,session):self.arguments={"checkConstraints":False,"keywords":[]}defwithin(self,container):self.container=containerreturnselfdefattach_file_containing(self,content,name=u"test.txt"):file_=NamedBlobFile(data=content,filename=name)self.arguments["file"]=file_returnselfdefcreate(self):obj=createContentInContainer(self.container,'opengever.document',**self.arguments)transaction.commit()returnobj

We can now use our Builder classes inside our test-case.

12345
frombuildersimportDossierBuilder,DocumentBuilderdossier=DossierBuilder().within(self.portal).titled(u"Dossier").create()document=DocumentBuilder().within(dossier).titled(u'Foobar') \
.attach_file_containing("lorem ipsum").create()

Already a big step forward! We have only defined two Builder classes, and we already notice some duplication among them. Defining a base class gives us a place to store the shared behavior:

123456789101112131415161718192021222324252627282930313233343536
classDexterityBuilder(object):def__init__(self,session):self.arguments={"checkConstraints":False}defwithin(self,container):self.container=containerreturnselfdeftitled(self,title):self.arguments["title"]=titlereturnselfdefcreate(self):obj=self.create_object()transaction.commit()returnobjclassDossierBuilder(DexterityBuilder):defcreate_object(self):returncreateContentInContainer(self.container,'opengever.dossier',**self.arguments)classDocumentBuilder(DexterityBuilder):defattach_file_containing(self,content,name=u"test.txt"):file_=NamedBlobFile(data=content,filename=name)self.arguments["file"]=file_returnselfdefcreate_object(self):returncreateContentInContainer(self.container,'opengever.document',**self.arguments)

Finally we hide the different Builder implementations and their instantiation behind a function. This allows us to change the Builders without touching all the tests that use them:

1234567
defBuilder(name):ifname=="dossier":returnDossierBuilder()elifname=="document":returnDocumentBuilder()else:raiseValueError("No Builder for %s"%name)

And the final code inside the test-cases looks like:

123456789
frombuildersimportBuilderdossier=Builder("dossier") \
.within(self.portal) \
.titled(u"Dossier").create()document=Builder("document") \
.within(dossier) \
.titled(u'Foobar') \
.attach_file_containing("lorem ipsum").create()

Check out this gist to view our current builders.py module. Let me know what you think and leave a comment.


Viewing all articles
Browse latest Browse all 3535

Trending Articles