Patchwork [PATCH/RFC,v3] Provide basic test fixture through inheritance

login
register
mail settings
Submitter Dirk Wallenstein
Date April 1, 2011, 5:50 p.m.
Message ID <20110401175007.GB19481@bottich>
Download mbox | patch
Permalink /patch/89313/
State Superseded
Headers show

Comments

Dirk Wallenstein - April 1, 2011, 5:50 p.m.
Provide project and user objects for test cases through inheritance.
Just derive from BaseFixtureTestCase instead of the normal TestCase.
The provided objects are accessible as attributes.

Signed-off-by: Dirk Wallenstein <halsmit@t-online.de>
---

With my new installation I've had errors if the user or user-profile
objects don't have consecutive "pk" starting at 1.  That's the only
change.

 apps/patchwork/fixtures/testbase.json |  142 +++++++++++++++++++++++++++++++++
 apps/patchwork/tests/patchparser.py   |   29 ++-----
 apps/patchwork/tests/utils.py         |   26 ++++++
 3 files changed, 177 insertions(+), 20 deletions(-)
 create mode 100644 apps/patchwork/fixtures/testbase.json
Jeremy Kerr - April 14, 2011, 3:24 a.m.
Hi Dirk,

> Provide project and user objects for test cases through inheritance.
> Just derive from BaseFixtureTestCase instead of the normal TestCase.
> The provided objects are accessible as attributes.

Looks good, seems like a good direction to go in.

Do you think this would work as a stand-alone object, rather than one
that is inherited by test cases? (ie, replacing the tests.utils.defaults
object) I already have some inheritance in the current tests, and would
rather avoid using multiple inheritance if at all possible.

Cheers,


Jeremy
Dirk Wallenstein - April 17, 2011, 8:05 a.m.
On Thu, Apr 14, 2011 at 11:24:45AM +0800, Jeremy Kerr wrote:
> Hi Dirk,
> 
> > Provide project and user objects for test cases through inheritance.
> > Just derive from BaseFixtureTestCase instead of the normal TestCase.
> > The provided objects are accessible as attributes.
> 
> Looks good, seems like a good direction to go in.
> 
> Do you think this would work as a stand-alone object, rather than one
> that is inherited by test cases? (ie, replacing the tests.utils.defaults
> object) I already have some inheritance in the current tests, and would
> rather avoid using multiple inheritance if at all possible.

Avoiding inheritance completely does not look to be feasible without
using a brittle "multiple interdependent statements" approach, because
the test case has to have a 'fixtures' attribute.  I can make it a Mixin
that is as invisible as possible and there won't be a diamond hierarchy.

In case a Mixin is ok...

Would you like to see name mangling with double underscores for the
attributes '__user', '__projects', etc?  And/Or would you prefer to have
all the attributes (like 'project_a') assembled in one additional
object? For access like:
    self.defaults.project_a

I've written a meta-type that assembles all 'fixtures' attributes from
all classes in the inheritance hierarchy into one list, but refrained
from proposing it in favor of explicitness.  Somebody who writes a test
case with multiple fixtures would have to know about clashes.  But it's
there if you like it.

Patch

diff --git a/apps/patchwork/fixtures/testbase.json b/apps/patchwork/fixtures/testbase.json
new file mode 100644
index 0000000..2591e12
--- /dev/null
+++ b/apps/patchwork/fixtures/testbase.json
@@ -0,0 +1,142 @@ 
+[
+    {
+        "pk": 1, 
+        "model": "auth.user", 
+        "fields": {
+            "username": "user_a", 
+            "first_name": "A", 
+            "last_name": "User", 
+            "is_active": true, 
+            "is_superuser": false, 
+            "is_staff": false, 
+            "last_login": "2011-02-28 20:08:51", 
+            "groups": [], 
+            "user_permissions": [], 
+            "password": "sha1$ccf11$82751d31568a5e6880cb3ae32d3726b29631d2da", 
+            "email": "user-a@test.net", 
+            "date_joined": "2011-02-28 20:08:51"
+        }
+    }, 
+    {
+        "pk": 2, 
+        "model": "auth.user", 
+        "fields": {
+            "username": "user_b", 
+            "first_name": "B", 
+            "last_name": "User", 
+            "is_active": true, 
+            "is_superuser": false, 
+            "is_staff": false, 
+            "last_login": "2011-02-28 20:10:21", 
+            "groups": [], 
+            "user_permissions": [], 
+            "password": "sha1$973b0$8474b91f6b0c87d946963da46e433ed2aae0b75b", 
+            "email": "user-b@test.net", 
+            "date_joined": "2011-02-28 20:10:21"
+        }
+    }, 
+    {
+        "pk": 3, 
+        "model": "auth.user", 
+        "fields": {
+            "username": "maintainer_a", 
+            "first_name": "A", 
+            "last_name": "Maintainer", 
+            "is_active": true, 
+            "is_superuser": false, 
+            "is_staff": true, 
+            "last_login": "2011-03-03 15:34:04", 
+            "groups": [], 
+            "user_permissions": [], 
+            "password": "sha1$04f46$90527a49e7ead0c92c4a2c74445d7e94f95c85f3", 
+            "email": "maintainer-a@test.net", 
+            "date_joined": "2011-03-03 15:34:04"
+        }
+    }, 
+    {
+        "pk": 4, 
+        "model": "auth.user", 
+        "fields": {
+            "username": "maintainer_b", 
+            "first_name": "B", 
+            "last_name": "Maintainer", 
+            "is_active": true, 
+            "is_superuser": false, 
+            "is_staff": true, 
+            "last_login": "2011-03-03 15:35:01", 
+            "groups": [], 
+            "user_permissions": [], 
+            "password": "sha1$9543d$ad920f6fbf1da12093a2f44a4f7d377d665e0a1c", 
+            "email": "maintainer-b@test.net", 
+            "date_joined": "2011-03-03 15:35:01"
+        }
+    }, 
+    {
+        "pk": 1, 
+        "model": "patchwork.userprofile", 
+        "fields": {
+            "maintainer_projects": [], 
+            "send_email": false, 
+            "patches_per_page": 100, 
+            "user": 1, 
+            "primary_project": 4
+        }
+    }, 
+    {
+        "pk": 2, 
+        "model": "patchwork.userprofile", 
+        "fields": {
+            "maintainer_projects": [], 
+            "send_email": false, 
+            "patches_per_page": 100, 
+            "user": 2, 
+            "primary_project": 5
+        }
+    }, 
+    {
+        "pk": 3, 
+        "model": "patchwork.userprofile", 
+        "fields": {
+            "maintainer_projects": [
+                4
+            ], 
+            "send_email": true, 
+            "patches_per_page": 100, 
+            "user": 3, 
+            "primary_project": 4
+        }
+    }, 
+    {
+        "pk": 4, 
+        "model": "patchwork.userprofile", 
+        "fields": {
+            "maintainer_projects": [
+                5
+            ], 
+            "send_email": true, 
+            "patches_per_page": 100, 
+            "user": 4, 
+            "primary_project": 5
+        }
+    }, 
+    {
+        "pk": 1, 
+        "model": "patchwork.project", 
+        "fields": {
+            "linkname": "project_a_link", 
+            "listemail": "project-a@test.net", 
+            "name": "project_a", 
+            "listid": "project-a.lists.test.net"
+        }
+    }, 
+    {
+        "pk": 2, 
+        "model": "patchwork.project", 
+        "fields": {
+            "linkname": "project_b_link", 
+            "listemail": "project-b@test.net", 
+            "name": "project_b", 
+            "listid": "project-b.lists.test.net"
+        }
+    }
+]
diff --git a/apps/patchwork/tests/patchparser.py b/apps/patchwork/tests/patchparser.py
index 7013e85..e3c0bb3 100644
--- a/apps/patchwork/tests/patchparser.py
+++ b/apps/patchwork/tests/patchparser.py
@@ -21,7 +21,8 @@  import unittest
 import os
 from email import message_from_string
 from patchwork.models import Project, Person, Patch, Comment
-from patchwork.tests.utils import read_patch, read_mail, create_email, defaults
+from patchwork.tests.utils import read_patch, read_mail, create_email, defaults, \
+        BaseFixtureTestCase
 
 try:
     from email.mime.text import MIMEText
@@ -235,7 +236,7 @@  class SenderCorrelationTest(unittest.TestCase):
     def tearDown(self):
         self.person.delete()
 
-class MultipleProjectPatchTest(unittest.TestCase):
+class MultipleProjectPatchTest(BaseFixtureTestCase):
     """ Test that patches sent to multiple patchwork projects are
         handled correctly """
 
@@ -244,33 +245,21 @@  class MultipleProjectPatchTest(unittest.TestCase):
     msgid = '<1@example.com>'
 
     def setUp(self):
-        self.p1 = Project(linkname = 'test-project-1', name = 'Project 1',
-                listid = '1.example.com', listemail='1@example.com')
-        self.p2 = Project(linkname = 'test-project-2', name = 'Project 2',
-                listid = '2.example.com', listemail='2@example.com')
-
-        self.p1.save()
-        self.p2.save()
-
         patch = read_patch(self.patch_filename)
         email = create_email(self.test_comment + '\n' + patch)
         email['Message-Id'] = self.msgid
 
         del email['List-ID']
-        email['List-ID'] = '<' + self.p1.listid + '>'
+        email['List-ID'] = '<' + self.project_a.listid + '>'
         parse_mail(email)
 
         del email['List-ID']
-        email['List-ID'] = '<' + self.p2.listid + '>'
+        email['List-ID'] = '<' + self.project_b.listid + '>'
         parse_mail(email)
 
     def testParsedProjects(self):
-        self.assertEquals(Patch.objects.filter(project = self.p1).count(), 1)
-        self.assertEquals(Patch.objects.filter(project = self.p2).count(), 1)
-
-    def tearDown(self):
-        self.p1.delete()
-        self.p2.delete()
+        self.assertEquals(Patch.objects.filter(project=self.project_a).count(), 1)
+        self.assertEquals(Patch.objects.filter(project=self.project_b).count(), 1)
 
 
 class MultipleProjectPatchCommentTest(MultipleProjectPatchTest):
@@ -283,7 +272,7 @@  class MultipleProjectPatchCommentTest(MultipleProjectPatchTest):
     def setUp(self):
         super(MultipleProjectPatchCommentTest, self).setUp()
 
-        for project in [self.p1, self.p2]:
+        for project in [self.project_a, self.project_b]:
             email = MIMEText(self.comment_content)
             email['From'] = defaults.sender
             email['Subject'] = defaults.subject
@@ -293,7 +282,7 @@  class MultipleProjectPatchCommentTest(MultipleProjectPatchTest):
             parse_mail(email)
 
     def testParsedComment(self):
-        for project in [self.p1, self.p2]:
+        for project in [self.project_a, self.project_b]:
             patch = Patch.objects.filter(project = project)[0]
             # we should see two comments now - the original mail with the patch,
             # and the one we parsed in setUp()
diff --git a/apps/patchwork/tests/utils.py b/apps/patchwork/tests/utils.py
index f1c95e8..078d982 100644
--- a/apps/patchwork/tests/utils.py
+++ b/apps/patchwork/tests/utils.py
@@ -21,6 +21,7 @@  import os
 import codecs
 from patchwork.models import Project, Person
 from django.contrib.auth.models import User
+import django.test
 
 from email import message_from_file
 try:
@@ -127,3 +128,28 @@  def create_email(content, subject = None, sender = None, multipart = False,
 
 
     return msg
+
+class BaseFixtureTestCase(django.test.TestCase):
+    """ Facilitate access to the objects provided in the testbase fixture. 
+
+        The objects listed in _projects and _users are directly accessible as
+        attributes.  Note that the access through attributes just wraps a
+        query.  This means that a local reference to call save() on is
+        necessary to apply changes.
+    """ 
+
+    fixtures = ['testbase']
+
+    # Two arbitrary projects
+    _projects = ['project_a', 'project_b']
+    # Two standard users with the corresponding project as primary project, and
+    # a maintainer for each project with staff status
+    _users = ['user_a', 'user_b', 'maintainer_a', 'maintainer_b']
+
+    def __getattr__(self, name):
+        if name in self._projects:
+            return Project.objects.get(name=name)
+        if name in self._users:
+            return User.objects.get(name=name)
+        else:
+            super(BaseFixtureTestCase, self).__getattr__(name)