diff --git a/lib/SimpleSAML/Utils/HttpAdapter.php b/lib/SimpleSAML/Utils/HttpAdapter.php
new file mode 100644
index 0000000000000000000000000000000000000000..71c611508ba17ea4351eb840a2bf7c95fa22ea4e
--- /dev/null
+++ b/lib/SimpleSAML/Utils/HttpAdapter.php
@@ -0,0 +1,211 @@
+<?php
+
+namespace SimpleSAML\Utils;
+
+/**
+ * Provides a non-static wrapper for the HTTP utility class.
+ *
+ * @package SimpleSAML\Utils
+ */
+class HttpAdapter
+{
+    /**
+     * @see HTTP::getServerHTTPS()
+     */
+    public function getServerHTTPS()
+    {
+        return HTTP::getServerHTTPS();
+    }
+
+    /**
+     * @see HTTP::getServerPort()
+     */
+    public function getServerPort()
+    {
+        return HTTP::getServerPort();
+    }
+
+    /**
+     * @see HTTP::addURLParameters()
+     */
+    public function addURLParameters($url, $parameters)
+    {
+        return HTTP::addURLParameters($url, $parameters);
+    }
+
+    /**
+     * @see HTTP::checkSessionCookie()
+     */
+    public function checkSessionCookie($retryURL = null)
+    {
+        HTTP::checkSessionCookie($retryURL);
+    }
+
+    /**
+     * @see HTTP::checkURLAllowed()
+     */
+    public function checkURLAllowed($url, array $trustedSites = null)
+    {
+        return HTTP::checkURLAllowed($url, $trustedSites);
+    }
+
+    /**
+     * @see HTTP::fetch()
+     */
+    public function fetch($url, $context = array(), $getHeaders = false)
+    {
+        return HTTP::fetch($url, $context, $getHeaders);
+    }
+
+    /**
+     * @see HTTP::getAcceptLanguage()
+     */
+    public function getAcceptLanguage()
+    {
+        return HTTP::getAcceptLanguage();
+    }
+
+    /**
+     * @see HTTP::guessBasePath()
+     */
+    public function guessBasePath()
+    {
+        return HTTP::guessBasePath();
+    }
+
+    /**
+     * @see HTTP::getBaseURL()
+     */
+    public function getBaseURL()
+    {
+        return HTTP::getBaseURL();
+    }
+
+    /**
+     * @see HTTP::getFirstPathElement()
+     */
+    public function getFirstPathElement($trailingslash = true)
+    {
+        return HTTP::getFirstPathElement($trailingslash);
+    }
+
+    /**
+     * @see HTTP::getPOSTRedirectURL()
+     */
+    public function getPOSTRedirectURL($destination, $data)
+    {
+        return HTTP::getPOSTRedirectURL($destination, $data);
+    }
+
+    /**
+     * @see HTTP::getSelfHost()
+     */
+    public function getSelfHost()
+    {
+        return HTTP::getSelfHost();
+    }
+
+    /**
+     * @see HTTP::getSelfHostWithNonStandardPort()
+     */
+    public function getSelfHostWithNonStandardPort()
+    {
+        return HTTP::getSelfHostWithNonStandardPort();
+    }
+
+    /**
+     * @see HTTP::getSelfHostWithPath()
+     */
+    public function getSelfHostWithPath()
+    {
+        return HTTP::getSelfHostWithPath();
+    }
+
+    /**
+     * @see HTTP::getSelfURL()
+     */
+    public function getSelfURL()
+    {
+        return HTTP::getSelfURL();
+    }
+
+    /**
+     * @see HTTP::getSelfURLHost()
+     */
+    public function getSelfURLHost()
+    {
+        return HTTP::getSelfURLHost();
+    }
+
+    /**
+     * @see HTTP::getSelfURLNoQuery()
+     */
+    public function getSelfURLNoQuery()
+    {
+        return HTTP::getSelfURLNoQuery();
+    }
+
+    /**
+     * @see HTTP::isHTTPS()
+     */
+    public function isHTTPS()
+    {
+        return HTTP::isHTTPS();
+    }
+
+    /**
+     * @see HTTP::normalizeURL()
+     */
+    public function normalizeURL($url)
+    {
+        return HTTP::normalizeURL($url);
+    }
+
+    /**
+     * @see HTTP::parseQueryString()
+     */
+    public function parseQueryString($query_string)
+    {
+        return HTTP::parseQueryString($query_string);
+    }
+
+    /**
+     * @see HTTP::redirectTrustedURL()
+     */
+    public function redirectTrustedURL($url, $parameters = array())
+    {
+        HTTP::redirectTrustedURL($url, $parameters);
+    }
+
+    /**
+     * @see HTTP::redirectUntrustedURL()
+     */
+    public function redirectUntrustedURL($url, $parameters = array())
+    {
+        HTTP::redirectUntrustedURL($url, $parameters);
+    }
+
+    /**
+     * @see HTTP::resolveURL()
+     */
+    public function resolveURL($url, $base = null)
+    {
+        return HTTP::resolveURL($url, $base);
+    }
+
+    /**
+     * @see HTTP::setCookie()
+     */
+    public function setCookie($name, $value, $params = null, $throw = true)
+    {
+        HTTP::setCookie($name, $value, $params, $throw);
+    }
+
+    /**
+     * @see HTTP::submitPOSTData()
+     */
+    public function submitPOSTData($destination, $data)
+    {
+        HTTP::submitPOSTData($destination, $data);
+    }
+}
diff --git a/modules/core/lib/Auth/Process/Cardinality.php b/modules/core/lib/Auth/Process/Cardinality.php
index 88b44f336be99fb4f4ec7ff5a987ea652c1f7f49..afc645cffb8de7f5f86b821829f6d6870a7378db 100644
--- a/modules/core/lib/Auth/Process/Cardinality.php
+++ b/modules/core/lib/Auth/Process/Cardinality.php
@@ -1,5 +1,7 @@
 <?php
 
+use SimpleSAML\Utils\HTTPAdapter;
+
 /**
  * Filter to ensure correct cardinality of attributes
  *
@@ -14,18 +16,24 @@ class sspmod_core_Auth_Process_Cardinality extends SimpleSAML_Auth_ProcessingFil
     /** @var array Entities that should be ignored */
     private $ignoreEntities = array();
 
+    /** @var HTTP */
+    private $http;
+
     /**
      * Initialize this filter, parse configuration.
      *
      * @param array $config  Configuration information about this filter.
      * @param mixed $reserved  For future use.
+     * @param HTTPAdapter $http  HTTP utility service (handles redirects).
      * @throws SimpleSAML_Error_Exception
      */
-    public function __construct($config, $reserved)
+    public function __construct($config, $reserved, HTTPAdapter $http = null)
     {
         parent::__construct($config, $reserved);
         assert(is_array($config));
 
+        $this->http = $http ?: new HTTPAdapter();
+
         foreach ($config as $attribute => $rules) {
             if ($attribute === '%ignoreEntities') {
                 $this->ignoreEntities = $config['%ignoreEntities'];
@@ -157,7 +165,7 @@ class sspmod_core_Auth_Process_Cardinality extends SimpleSAML_Auth_ProcessingFil
         if (array_key_exists('core:cardinality:errorAttributes', $request)) {
             $id = SimpleSAML_Auth_State::saveState($request, 'core:cardinality');
             $url = SimpleSAML\Module::getModuleURL('core/cardinality_error.php');
-            \SimpleSAML\Utils\HTTP::redirectTrustedURL($url, array('StateId' => $id));
+            $this->http->redirectTrustedURL($url, array('StateId' => $id));
             return;
         }
     }
diff --git a/modules/core/lib/Auth/Process/CardinalitySingle.php b/modules/core/lib/Auth/Process/CardinalitySingle.php
index a8dbb3c56042dec1d9bf9f7d2503bc91d396237e..173d95c5c48e32c2e9909b07131ccd80692dbcbb 100644
--- a/modules/core/lib/Auth/Process/CardinalitySingle.php
+++ b/modules/core/lib/Auth/Process/CardinalitySingle.php
@@ -1,5 +1,7 @@
 <?php
 
+use SimpleSAML\Utils\HttpAdapter;
+
 /**
  * Filter to ensure correct cardinality of single-valued attributes
  *
@@ -26,17 +28,23 @@ class sspmod_core_Auth_Process_CardinalitySingle extends SimpleSAML_Auth_Process
     /** @var array Entities that should be ignored */
     private $ignoreEntities = array();
 
+    /** @var HTTP */
+    private $http;
+
     /**
      * Initialize this filter, parse configuration.
      *
      * @param array $config  Configuration information about this filter.
      * @param mixed $reserved  For future use.
+     * @param HTTPAdapter $http  HTTP utility service (handles redirects).
      */
-    public function __construct($config, $reserved)
+    public function __construct($config, $reserved, HTTPAdapter $http = null)
     {
         parent::__construct($config, $reserved);
         assert(is_array($config));
 
+        $this->http = $http ?: new HTTPAdapter();
+
         if (array_key_exists('singleValued', $config)) {
             $this->singleValued = $config['singleValued'];
         }
@@ -102,7 +110,7 @@ class sspmod_core_Auth_Process_CardinalitySingle extends SimpleSAML_Auth_Process
         if (array_key_exists('core:cardinality:errorAttributes', $request)) {
             $id = SimpleSAML_Auth_State::saveState($request, 'core:cardinality');
             $url = SimpleSAML\Module::getModuleURL('core/cardinality_error.php');
-            \SimpleSAML\Utils\HTTP::redirectTrustedURL($url, array('StateId' => $id));
+            $this->http->redirectTrustedURL($url, array('StateId' => $id));
             return;
         }
     }
diff --git a/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php b/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php
index c66f531449a8c105909ec5ba9da8da583c22d7c8..c3085154d905919768de25aff5e419fa74f0077b 100644
--- a/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php
+++ b/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php
@@ -9,6 +9,8 @@ if (class_exists('\PHPUnit\Framework\TestCase', true) and !class_exists('\PHPUni
  */
 class Test_Core_Auth_Process_CardinalitySingleTest extends \PHPUnit_Framework_TestCase
 {
+    private $http;
+
     /**
      * Helper function to run the filter with a given configuration.
      *
@@ -16,11 +18,11 @@ class Test_Core_Auth_Process_CardinalitySingleTest extends \PHPUnit_Framework_Te
      * @param  array $request The request state.
      * @return array  The state array after processing.
      */
-    private static function processFilter(array $config, array $request)
+    private function processFilter(array $config, array $request)
     {
         $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
         $_SERVER['REQUEST_METHOD'] = 'GET';
-        $filter = new sspmod_core_Auth_Process_CardinalitySingle($config, null);
+        $filter = new sspmod_core_Auth_Process_CardinalitySingle($config, null, $this->http);
         $filter->process($request);
         return $request;
     }
@@ -28,6 +30,9 @@ class Test_Core_Auth_Process_CardinalitySingleTest extends \PHPUnit_Framework_Te
     protected function setUp()
     {
         \SimpleSAML_Configuration::loadFromArray(array(), '[ARRAY]', 'simplesaml');
+        $this->http = $this->getMockBuilder('SimpleSAML\Utils\HTTPAdapter')
+                           ->setMethods(array('redirectTrustedURL'))
+                           ->getMock();
     }
 
     /**
@@ -43,7 +48,7 @@ class Test_Core_Auth_Process_CardinalitySingleTest extends \PHPUnit_Framework_Te
                 'eduPersonPrincipalName' => array('joe@example.com'),
             ),
         );
-        $result = self::processFilter($config, $request);
+        $result = $this->processFilter($config, $request);
         $attributes = $result['Attributes'];
         $expectedData = array('eduPersonPrincipalName' => array('joe@example.com'));
         $this->assertEquals($expectedData, $attributes, "Assertion values should not have changed");
@@ -62,7 +67,7 @@ class Test_Core_Auth_Process_CardinalitySingleTest extends \PHPUnit_Framework_Te
                 'eduPersonPrincipalName' => array('joe@example.com', 'bob@example.net'),
             ),
         );
-        $result = self::processFilter($config, $request);
+        $result = $this->processFilter($config, $request);
         $attributes = $result['Attributes'];
         $expectedData = array('eduPersonPrincipalName' => array('joe@example.com'));
         $this->assertEquals($expectedData, $attributes, "Only first value should be returned");
@@ -78,7 +83,7 @@ class Test_Core_Auth_Process_CardinalitySingleTest extends \PHPUnit_Framework_Te
                 'eduPersonPrincipalName' => array('joe@example.com'),
             ),
         );
-        $result = self::processFilter($config, $request);
+        $result = $this->processFilter($config, $request);
         $attributes = $result['Attributes'];
         $expectedData = array('eduPersonPrincipalName' => array('joe@example.com'));
         $this->assertEquals($expectedData, $attributes, "Assertion values should not have changed");
@@ -98,7 +103,7 @@ class Test_Core_Auth_Process_CardinalitySingleTest extends \PHPUnit_Framework_Te
                 'eduPersonPrincipalName' => array('joe@example.com', 'bob@example.net'),
             ),
         );
-        $result = self::processFilter($config, $request);
+        $result = $this->processFilter($config, $request);
         $attributes = $result['Attributes'];
         $expectedData = array('eduPersonPrincipalName' => array('joe@example.com|bob@example.net'));
         $this->assertEquals($expectedData, $attributes, "Flattened string should be returned");
@@ -115,7 +120,7 @@ class Test_Core_Auth_Process_CardinalitySingleTest extends \PHPUnit_Framework_Te
                 'eduPersonPrincipalName' => array('joe@example.com'),
             ),
         );
-        $result = self::processFilter($config, $request);
+        $result = $this->processFilter($config, $request);
         $attributes = $result['Attributes'];
         $expectedData = array('eduPersonPrincipalName' => array('joe@example.com'));
         $this->assertEquals($expectedData, $attributes, "Assertion values should not have changed");
@@ -123,13 +128,9 @@ class Test_Core_Auth_Process_CardinalitySingleTest extends \PHPUnit_Framework_Te
 
     /**
      * Test abort
-     * @expectedException PHPUnit_Framework_Error
-     * @expectedExceptionMessageRegExp /REQUEST_URI/
      */
     public function testAbort()
     {
-        $this->markTestSkipped('Breaks testsuite see #860');
-
         $config = array(
             'singleValued' => array('eduPersonPrincipalName'),
         );
@@ -138,6 +139,10 @@ class Test_Core_Auth_Process_CardinalitySingleTest extends \PHPUnit_Framework_Te
                 'eduPersonPrincipalName' => array('joe@example.com', 'bob@example.net'),
             ),
         );
-        self::processFilter($config, $request);
+
+        $this->http->expects($this->once())
+                   ->method('redirectTrustedURL');
+
+        $this->processFilter($config, $request);
     }
 }
diff --git a/tests/modules/core/lib/Auth/Process/CardinalityTest.php b/tests/modules/core/lib/Auth/Process/CardinalityTest.php
index 5aa20d0801d605a958ffa172e6ed3784805508bd..5231fce8cbd4abd49d19a43c9975ad939badf53f 100644
--- a/tests/modules/core/lib/Auth/Process/CardinalityTest.php
+++ b/tests/modules/core/lib/Auth/Process/CardinalityTest.php
@@ -9,6 +9,8 @@ if (class_exists('\PHPUnit\Framework\TestCase', true) and !class_exists('\PHPUni
  */
 class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
 {
+    private $http;
+
     /**
      * Helper function to run the filter with a given configuration.
      *
@@ -16,11 +18,11 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
      * @param  array $request The request state.
      * @return array  The state array after processing.
      */
-    private static function processFilter(array $config, array $request)
+    private function processFilter(array $config, array $request)
     {
         $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
         $_SERVER['REQUEST_METHOD'] = 'GET';
-        $filter = new sspmod_core_Auth_Process_Cardinality($config, null);
+        $filter = new sspmod_core_Auth_Process_Cardinality($config, null, $this->http);
         $filter->process($request);
         return $request;
     }
@@ -28,6 +30,9 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
     protected function setUp()
     {
         \SimpleSAML_Configuration::loadFromArray(array(), '[ARRAY]', 'simplesaml');
+        $this->http = $this->getMockBuilder('SimpleSAML\Utils\HTTPAdapter')
+                           ->setMethods(array('redirectTrustedURL'))
+                           ->getMock();
     }
 
     /*
@@ -43,7 +48,7 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
                 'mail' => array('joe@example.com', 'bob@example.com'),
             ),
         );
-        $result = self::processFilter($config, $request);
+        $result = $this->processFilter($config, $request);
         $attributes = $result['Attributes'];
         $expectedData = array('mail' => array('joe@example.com', 'bob@example.com'));
         $this->assertEquals($expectedData, $attributes, "Assertion values should not have changed");
@@ -62,7 +67,7 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
                 'mail' => array('joe@example.com', 'bob@example.com'),
             ),
         );
-        $result = self::processFilter($config, $request);
+        $result = $this->processFilter($config, $request);
         $attributes = $result['Attributes'];
         $expectedData = array('mail' => array('joe@example.com', 'bob@example.com'));
         $this->assertEquals($expectedData, $attributes, "Assertion values should not have changed");
@@ -81,7 +86,7 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
                 'mail' => array('joe@example.com', 'bob@example.com'),
             ),
         );
-        $result = self::processFilter($config, $request);
+        $result = $this->processFilter($config, $request);
         $attributes = $result['Attributes'];
         $expectedData = array('mail' => array('joe@example.com', 'bob@example.com'));
         $this->assertEquals($expectedData, $attributes, "Assertion values should not have changed");
@@ -89,12 +94,9 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
 
     /**
      * Test maximum is out of bounds results in redirect
-     * @expectedException PHPUnit_Framework_Error
-     * @expectedExceptionMessageRegExp /REQUEST_URI/
      */
     public function testMaxOutOfBounds()
     {
-        $this->markTestSkipped('Breaks testsuite see #860');
         $config = array(
             'mail' => array('max' => 2),
         );
@@ -103,17 +105,18 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
                 'mail' => array('joe@example.com', 'bob@example.com', 'fred@example.com'),
             ),
         );
-        self::processFilter($config, $request);
+
+        $this->http->expects($this->once())
+                   ->method('redirectTrustedURL');
+
+        $this->processFilter($config, $request);
     }
 
     /**
      * Test minimum is out of bounds results in redirect
-     * @expectedException PHPUnit_Framework_Error
-     * @expectedExceptionMessageRegExp /REQUEST_URI/
      */
     public function testMinOutOfBounds()
     {
-        $this->markTestSkipped('Breaks testsuite see #860');
         $config = array(
             'mail' => array('min' => 3),
         );
@@ -122,24 +125,29 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
                 'mail' => array('joe@example.com', 'bob@example.com'),
             ),
         );
-        self::processFilter($config, $request);
+
+        $this->http->expects($this->once())
+                   ->method('redirectTrustedURL');
+
+        $this->processFilter($config, $request);
     }
 
     /**
      * Test missing attribute results in redirect
-     * @expectedException PHPUnit_Framework_Error
-     * @expectedExceptionMessageRegExp /REQUEST_URI/
      */
     public function testMissingAttribute()
     {
-        $this->markTestSkipped('Breaks testsuite see #860');
         $config = array(
             'mail' => array('min' => 1),
         );
         $request = array(
             'Attributes' => array( ),
         );
-        self::processFilter($config, $request);
+
+        $this->http->expects($this->once())
+                   ->method('redirectTrustedURL');
+
+        $this->processFilter($config, $request);
     }
 
     /*
@@ -153,7 +161,6 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
      */
     public function testMinInvalid()
     {
-        $this->markTestSkipped('Breaks testsuite see #860');
         $config = array(
             'mail' => array('min' => false),
         );
@@ -162,7 +169,7 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
                 'mail' => array('joe@example.com', 'bob@example.com'),
             ),
         );
-        self::processFilter($config, $request);
+        $this->processFilter($config, $request);
     }
 
     /**
@@ -172,7 +179,6 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
      */
     public function testMinNegative()
     {
-        $this->markTestSkipped('Breaks testsuite see #860');
         $config = array(
             'mail' => array('min' => -1),
         );
@@ -181,7 +187,7 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
                 'mail' => array('joe@example.com', 'bob@example.com'),
             ),
         );
-        self::processFilter($config, $request);
+        $this->processFilter($config, $request);
     }
 
     /**
@@ -191,7 +197,6 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
      */
     public function testMaxInvalid()
     {
-        $this->markTestSkipped('Breaks testsuite see #860');
         $config = array(
             'mail' => array('max' => false),
         );
@@ -200,7 +205,7 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
                 'mail' => array('joe@example.com', 'bob@example.com'),
             ),
         );
-        self::processFilter($config, $request);
+        $this->processFilter($config, $request);
     }
 
     /**
@@ -210,7 +215,6 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
      */
     public function testMinGreaterThanMax()
     {
-        $this->markTestSkipped('Breaks testsuite see #860');
         $config = array(
             'mail' => array('min' => 2, 'max' => 1),
         );
@@ -219,7 +223,7 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
                 'mail' => array('joe@example.com', 'bob@example.com'),
             ),
         );
-        self::processFilter($config, $request);
+        $this->processFilter($config, $request);
     }
 
     /**
@@ -229,7 +233,6 @@ class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_TestCase
      */
     public function testInvalidAttributeName()
     {
-        $this->markTestSkipped('Breaks testsuite see #860');
         $config = array(
             array('min' => 2, 'max' => 1),
         );
diff --git a/tools/phpunit/phpunit.xml b/tools/phpunit/phpunit.xml
index b82bc2077bff517bf5f5c806da56498b6bf68133..eae5444c383cc978cf85035c7d5f100425ed95b2 100644
--- a/tools/phpunit/phpunit.xml
+++ b/tools/phpunit/phpunit.xml
@@ -23,6 +23,7 @@
             <exclude>
                 <directory>./../../vendor/</directory>
                 <directory>./../../tests/</directory>
+                <file>./../../lib/SimpleSAML/Utils/HttpAdapter.php</file>
             </exclude>
         </whitelist>
     </filter>