diff --git a/src/postorius/doc/news.rst b/src/postorius/doc/news.rst
index eb121da..859f13a 100644
--- a/src/postorius/doc/news.rst
+++ b/src/postorius/doc/news.rst
@@ -25,6 +25,7 @@
 (2014-xx-xx)
 
 * French translation. Provided by Guillaume Libersat
+* Addedd an improved test harness using WebTest. Contributed by Aurélien Abompard.
 
 
 1.0 beta 1 -- "Year of the Parrot"
diff --git a/src/postorius/management/commands/mmclient.py b/src/postorius/management/commands/mmclient.py
index 31a4104..2145d05 100644
--- a/src/postorius/management/commands/mmclient.py
+++ b/src/postorius/management/commands/mmclient.py
@@ -18,7 +18,6 @@
 
 from django.conf import settings
 from django.core.management.base import BaseCommand, CommandError
-from mailmanclient import Client, MailmanConnectionError
 from postorius import utils
 from urllib2 import HTTPError
 
diff --git a/src/postorius/tests/mailman_api_tests/__init__.py b/src/postorius/tests/mailman_api_tests/__init__.py
index edeaacc..c4d5c4e 100644
--- a/src/postorius/tests/mailman_api_tests/__init__.py
+++ b/src/postorius/tests/mailman_api_tests/__init__.py
@@ -14,17 +14,34 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # Postorius.  If not, see .
-from django.conf import settings
 
-from postorius.tests.mm_setup import setup_mm, teardown_mm, Testobject
+from mailmanclient.tests.utils import FakeMailmanClient
 
-
-test_obj = Testobject()
+from mock import patch
+from django.test import TestCase
 
 
 def setup_module():
-    setup_mm(test_obj)
-
+    FakeMailmanClient.setUp()
 
 def teardown_module():
-    teardown_mm(test_obj)
+    FakeMailmanClient.tearDown()
+
+
+class MMTestCase(TestCase):
+
+    def _pre_setup(self):
+        super(MMTestCase, self)._pre_setup()
+        self.mm_client = FakeMailmanClient(
+            'http://localhost:8001/3.0', "restadmin", "restpass")
+        self.mm_client_patcher = patch('postorius.utils.Client', lambda *a, **kw: self.mm_client)
+        self.mm_client_patcher.start()
+        self.mm_client.create_domain(
+            'example.com',
+            contact_address='postmaster@example.com',
+            base_url='lists.example.com')
+
+    def _post_teardown(self):
+        self.mm_client_patcher.stop()
+        self.mm_client.delete_domain('example.com')
+        super(MMTestCase, self)._post_teardown()
diff --git a/src/postorius/tests/mailman_api_tests/test_list_index.py b/src/postorius/tests/mailman_api_tests/test_list_index.py
index ff3629a..122e5a3 100644
--- a/src/postorius/tests/mailman_api_tests/test_list_index.py
+++ b/src/postorius/tests/mailman_api_tests/test_list_index.py
@@ -14,42 +14,22 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # Postorius.  If not, see .
+
 import logging
 
 from django.core.urlresolvers import reverse
-from django.test import TestCase
-from django.test.client import Client
-from django.test.utils import override_settings
 
-from postorius.tests.mm_setup import mm_client
+from postorius.tests.mailman_api_tests import MMTestCase
 
 
 logger = logging.getLogger(__name__)
 
 
-def setup_module():
-    # Create a domain for all tests in this module.
-    mm_client.create_domain(
-        'example.com',
-        contact_address='postmaster@example.com',
-        base_url='lists.example.com')
-
-
-def teardown_module():
-    # Clean up.
-    mm_client.delete_domain('example.com')
-
-
-@override_settings(
-    MAILMAN_API_URL='http://localhost:9001',
-    MAILMAN_USER='restadmin',
-    MAILMAN_PASS='restpass')
-class ListIndexPageTest(TestCase):
+class ListIndexPageTest(MMTestCase):
     """Tests for the list index page."""
 
     def setUp(self):
-        self.client = Client()
-        domain = mm_client.get_domain('example.com')
+        domain = self.mm_client.get_domain('example.com')
         self.foo_list = domain.create_list('foo')
 
     def tearDown(self):
diff --git a/src/postorius/tests/mailman_api_tests/test_list_members.py b/src/postorius/tests/mailman_api_tests/test_list_members.py
index a405821..914109c 100644
--- a/src/postorius/tests/mailman_api_tests/test_list_members.py
+++ b/src/postorius/tests/mailman_api_tests/test_list_members.py
@@ -18,42 +18,21 @@
 
 from django.contrib.auth.models import User
 from django.core.urlresolvers import reverse
-from django.test import TestCase
-from django.test.client import Client
-from django.test.utils import override_settings
 
-from postorius.tests.mm_setup import mm_client
+from postorius.tests.mailman_api_tests import MMTestCase
 
 
 logger = logging.getLogger(__name__)
 
 
-def setup_module():
-    # Create a domain for all tests in this module.
-    mm_client.create_domain(
-        'example.com',
-        contact_address='postmaster@example.com',
-        base_url='lists.example.com')
-
-
-def teardown_module():
-    # Clean up.
-    mm_client.delete_domain('example.com')
-
-
-@override_settings(
-    MAILMAN_API_URL='http://localhost:9001',
-    MAILMAN_USER='restadmin',
-    MAILMAN_PASS='restpass')
-class ListMembersPageTest(TestCase):
+class ListMembersPageTest(MMTestCase):
     """Tests for the list members page.
 
     Tests permissions and creation of list owners and moderators.
     """
 
     def setUp(self):
-        self.client = Client()
-        domain = mm_client.get_domain('example.com')
+        domain = self.mm_client.get_domain('example.com')
         self.foo_list = domain.create_list('foo')
         self.user = User.objects.create_user('testuser', 'test@example.com',
                                              'testpass')
diff --git a/src/postorius/tests/mailman_api_tests/test_list_new.py b/src/postorius/tests/mailman_api_tests/test_list_new.py
index 0989ccb..8df4373 100644
--- a/src/postorius/tests/mailman_api_tests/test_list_new.py
+++ b/src/postorius/tests/mailman_api_tests/test_list_new.py
@@ -19,38 +19,17 @@
 
 from django.contrib.auth.models import User
 from django.core.urlresolvers import reverse
-from django.test import TestCase
-from django.test.client import Client
-from django.test.utils import override_settings
 
-from postorius.tests.mm_setup import mm_client
+from postorius.tests.mailman_api_tests import MMTestCase
 
 
 logger = logging.getLogger(__name__)
 
 
-def setup_module():
-    # Create a domain for all tests in this module.
-    mm_client.create_domain(
-        'example.com',
-        contact_address='postmaster@example.com',
-        base_url='lists.example.com')
-
-
-def teardown_module():
-    # Clean up.
-    mm_client.delete_domain('example.com')
-
-
-@override_settings(
-    MAILMAN_API_URL='http://localhost:9001',
-    MAILMAN_USER='restadmin',
-    MAILMAN_PASS='restpass')
-class ListCreationTest(TestCase):
+class ListCreationTest(MMTestCase):
     """Tests for the new list page."""
 
     def setUp(self):
-        self.client = Client()
         self.user = User.objects.create_user('user', 'user@example.com', 'pwd')
         self.superuser = User.objects.create_superuser('su', 'su@example.com',
                                                        'pwd')
@@ -66,11 +45,6 @@
             response,
             '/postorius/accounts/login/?next=/postorius/lists/new/')
 
-    def test_page_accessible_to_su(self):
-        self.client.login(username='su', password='pwd')
-        response = self.client.get(reverse('list_new'))
-        self.assertEqual(response.status_code, 200)
-
     def test_new_list_created(self):
         self.client.login(username='su', password='pwd')
         post_data = {'listname': 'a_new_list',
@@ -79,5 +53,5 @@
                      'advertised': 'True',
                      'description': 'A new list.'}
         self.client.post(reverse('list_new'), post_data)
-        a_new_list = mm_client.get_list('a_new_list@example.com')
+        a_new_list = self.mm_client.get_list('a_new_list@example.com')
         self.assertEqual(a_new_list.fqdn_listname, u'a_new_list@example.com')
diff --git a/src/postorius/tests/mailman_api_tests/test_list_summary.py b/src/postorius/tests/mailman_api_tests/test_list_summary.py
index 919eaa1..85212bf 100644
--- a/src/postorius/tests/mailman_api_tests/test_list_summary.py
+++ b/src/postorius/tests/mailman_api_tests/test_list_summary.py
@@ -18,34 +18,14 @@
 
 from django.contrib.auth.models import User
 from django.core.urlresolvers import reverse
-from django.test import TestCase
-from django.test.client import Client
-from django.test.utils import override_settings
 
-from postorius.tests.mm_setup import mm_client
+from postorius.tests.mailman_api_tests import MMTestCase
 
 
 logger = logging.getLogger(__name__)
 
 
-def setup_module():
-    # Create a domain for all tests in this module.
-    mm_client.create_domain(
-        'example.com',
-        contact_address='postmaster@example.com',
-        base_url='lists.example.com')
-
-
-def teardown_module():
-    # Clean up.
-    mm_client.delete_domain('example.com')
-
-
-@override_settings(
-    MAILMAN_API_URL='http://localhost:9001',
-    MAILMAN_USER='restadmin',
-    MAILMAN_PASS='restpass')
-class ListSummaryPageTest(TestCase):
+class ListSummaryPageTest(MMTestCase):
     """Tests for the list summary page.
 
     Tests accessiblity and existince of the submit form depending on
@@ -53,8 +33,7 @@
     """
 
     def setUp(self):
-        self.client = Client()
-        domain = mm_client.get_domain('example.com')
+        domain = self.mm_client.get_domain('example.com')
         self.foo_list = domain.create_list('foo')
 
     def tearDown(self):
diff --git a/src/postorius/tests/mm_setup.py b/src/postorius/tests/mm_setup.py
deleted file mode 100644
index f88df53..0000000
--- a/src/postorius/tests/mm_setup.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (C) 1998-2014 by the Free Software Foundation, Inc.
-#
-# This file is part of Postorius.
-#
-# Postorius is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free
-# Software Foundation, either version 3 of the License, or (at your option)
-# any later version.
-#
-# Postorius is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Postorius.  If not, see .
-import os
-import time
-import shutil
-import logging
-import tempfile
-import subprocess
-
-from django.conf import settings
-from mailmanclient import Client
-
-
-logger = logging.getLogger(__name__)
-
-
-mm_client = Client('%s/3.0' % settings.MAILMAN_TEST_API_URL,
-                   settings.MAILMAN_TEST_USER,
-                   settings.MAILMAN_TEST_PASS)
-
-
-class Testobject:
-    bindir = None
-    vardir = None
-    cfgfile = None
-
-
-def setup_mm(testobject):
-    bindir = testobject.bindir = settings.MAILMAN_TEST_BINDIR
-    if bindir is None:
-        raise RuntimeError("something's not quite right")
-    vardir = testobject.vardir = tempfile.mkdtemp()
-    cfgfile = testobject.cfgfile = os.path.join(vardir, 'client_test.cfg')
-    with open(cfgfile, 'w') as fp:
-        print >> fp, """\
-[mailman]
-layout: tmpdir
-[paths.tmpdir]
-var_dir: {vardir}
-log_dir: /tmp/mmclient/logs
-[runner.archive]
-start: no
-[runner.bounces]
-start: no
-[runner.command]
-start: no
-[runner.in]
-start: no
-[runner.lmtp]
-start: no
-[runner.news]
-start: no
-[runner.out]
-start: no
-[runner.pipeline]
-start: no
-[runner.retry]
-start: no
-[runner.virgin]
-start: no
-[runner.digest]
-start: no
-[webservice]
-port: 9001
-""".format(vardir=vardir)
-    mailman = os.path.join(bindir, 'mailman')
-    subprocess.call([mailman, '-C', cfgfile, 'start', '-q'])
-    time.sleep(3)
-    return testobject
-
-
-def teardown_mm(testobject):
-    bindir = testobject.bindir
-    cfgfile = testobject.cfgfile
-    vardir = testobject.vardir
-    mailman = os.path.join(bindir, 'mailman')
-    subprocess.call([mailman, '-C', cfgfile, 'stop', '-q'])
-    shutil.rmtree(vardir)
diff --git a/src/postorius/tests/test_address_activation.py b/src/postorius/tests/test_address_activation.py
index 02fef05..b09ee44 100644
--- a/src/postorius/tests/test_address_activation.py
+++ b/src/postorius/tests/test_address_activation.py
@@ -9,7 +9,6 @@
 from django.test.client import Client, RequestFactory
 from django.test.utils import override_settings
 from django.utils import unittest
-from mailmanclient._client import _Connection
 from mock import patch, call
 
 from postorius.forms import AddressActivationForm
diff --git a/src/postorius/views/api.py b/src/postorius/views/api.py
index 2d2f62d..8856df6 100644
--- a/src/postorius/views/api.py
+++ b/src/postorius/views/api.py
@@ -39,7 +39,6 @@
 from django.utils.translation import gettext as _
 from urllib2 import HTTPError
 
-from mailmanclient import Client
 from postorius import utils
 from postorius.models import (Domain, List, Member, MailmanUser,
                               MailmanApiError, Mailman404Error)
diff --git a/src/postorius/views/generic.py b/src/postorius/views/generic.py
index f523837..f491662 100644
--- a/src/postorius/views/generic.py
+++ b/src/postorius/views/generic.py
@@ -21,7 +21,6 @@
 from django.shortcuts import render_to_response, redirect
 from django.template import Context, loader, RequestContext
 from django.views.generic import TemplateView, View
-from mailmanclient import Client
 
 from postorius.models import (Domain, List, Member, MailmanUser,
                               MailmanApiError, Mailman404Error)
diff --git a/src/postorius/views/settings.py b/src/postorius/views/settings.py
index aeed78f..3859aec 100644
--- a/src/postorius/views/settings.py
+++ b/src/postorius/views/settings.py
@@ -37,7 +37,6 @@
 from django.utils.translation import gettext as _
 from urllib2 import HTTPError
 
-from mailmanclient import Client
 from postorius import utils
 from postorius.models import (Domain, List, Member, MailmanUser,
                               MailmanApiError, Mailman404Error)
diff --git a/src/postorius/views/views.py b/src/postorius/views/views.py
index 400b0c5..ebdf28c 100644
--- a/src/postorius/views/views.py
+++ b/src/postorius/views/views.py
@@ -40,7 +40,6 @@
 from django.utils.translation import gettext as _
 from urllib2 import HTTPError
 
-from mailmanclient import Client
 from postorius import utils
 from postorius.models import (Domain, List, Member, MailmanUser,
                               MailmanApiError, Mailman404Error)