diff --git a/lib/SimpleSAML/HTTP/RunnableResponse.php b/lib/SimpleSAML/HTTP/RunnableResponse.php
index 2692c75f15f1c341f1520bb0ec14e63dc128a919..ea575f3bd37ac0cfeacdc344cccd7fbc60343c99 100644
--- a/lib/SimpleSAML/HTTP/RunnableResponse.php
+++ b/lib/SimpleSAML/HTTP/RunnableResponse.php
@@ -31,6 +31,7 @@ class RunnableResponse extends Response
     {
         $this->arguments = $args;
         $this->callable = $callable;
+        $this->charset = 'UTF-8';
         parent::__construct();
     }
 
diff --git a/lib/SimpleSAML/XHTML/Template.php b/lib/SimpleSAML/XHTML/Template.php
index 2baeecdfbd1ebe5c53cd78e8d06873f8ba48b232..3df5866a1ad0d49f19820b4ab009c2258ee61ce5 100644
--- a/lib/SimpleSAML/XHTML/Template.php
+++ b/lib/SimpleSAML/XHTML/Template.php
@@ -66,6 +66,7 @@ class Template extends Response
      * The twig environment.
      *
      * @var \Twig\Environment
+     * @psalm-suppress PropertyNotSetInConstructor  Remove this annotation in 2.0
      */
     private $twig;
 
@@ -155,6 +156,8 @@ class Template extends Response
 
             $this->twig = $this->setupTwig();
         }
+
+        $this->charset = 'UTF-8';
         parent::__construct();
     }
 
diff --git a/psalm.xml b/psalm.xml
index 9d3da2f5c9407bbbe2ec57dfc74cc6515ee7ab00..64c381171cd01df7d80176db249f6243693af672 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -20,6 +20,10 @@
         <directory name="lib/SimpleSAML/Utils" />
         <directory name="lib/SimpleSAML/XHTML" />
         <directory name="lib/SimpleSAML/XML" />
+
+        <!-- Replaces all modules/... with this one-liner for 2.0
+        <directory name="modules" />
+        -->
         <directory name="modules/admin" />
         <directory name="modules/core" />
         <directory name="modules/cron" />
@@ -28,6 +32,8 @@
         <directory name="modules/portal" />
         <directory name="modules/saml" />
 
+        <directory name="tests" />
+
         <!-- Ignore deprecated classes -->
         <ignoreFiles>
             <file name="lib/SimpleSAML/Auth/DefaultAuth.php" />
@@ -37,6 +43,9 @@
             <file name="modules/**/templates/*.tpl.php" />
             <file name="modules/saml/templates/proxy/*.tpl.php" />
             <file name="modules/saml/templates/sp/*.tpl.php" />
+
+            <directory name="tests/Utils/Stubs" />
+            <directory name="vendor" />
         </ignoreFiles>
     </projectFiles>
 
@@ -77,6 +86,8 @@
                 <file name="bin/*.php" />
                 <file name="lib/SimpleSAML/XHTML/Template.php" />
                 <file name="modules/*/bin/*.php" />
+                <file name="tests/bootstrap.php" />
+                <file name="tests/routers/configLoader.php" />
             </errorLevel>
         </UnresolvableInclude>
 
@@ -87,6 +98,13 @@
                 <file name="modules/*/www/*.php" />
             </errorLevel>
         </MissingFile>
+
+        <!-- Suppress PHPunit-issue -->
+        <PropertyNotSetInConstructor>
+            <errorLevel type="suppress">
+                <directory name="tests" />
+            </errorLevel>
+        </PropertyNotSetInConstructor>
     </issueHandlers>
 
     <stubs>
diff --git a/tests/BuiltInServer.php b/tests/BuiltInServer.php
index 1937ca9281661b9b9e03fd89895cb5abe54fe374..143e35fdf69ca008aad628d312af9838a9ae45a1 100644
--- a/tests/BuiltInServer.php
+++ b/tests/BuiltInServer.php
@@ -25,7 +25,7 @@ class BuiltInServer
      *
      * @var string
      */
-    protected $address;
+    protected $address = 'example.org';
 
     /**
      * The name of a "router" file to run for every request performed to this server.
@@ -72,7 +72,7 @@ class BuiltInServer
      * Start the built-in server in a random port.
      *
      * This method will wait up to 5 seconds for the server to start. When it returns an address, it is guaranteed that
-     * the server has started and is listening for connections. If it returns false on the other hand, there will be no
+     * the server has started and is listening for connections. If it returns the default value on the other hand, there will be no
      * guarantee that the server started properly.
      *
      * @return string The address where the server is listening for connections, or false if the server failed to start
@@ -108,14 +108,17 @@ class BuiltInServer
 
         // wait until it's listening for connections to avoid race conditions
         $start = microtime(true);
-        while (($sock = @fsockopen('localhost', $port, $errno, $errstr, 10)) === false) {
-            // set a 5 secs timeout waiting for the server to start
-            if (microtime(true) > $start + 5) {
-                $this->pid = false; // signal failure
-                $this->address = false;
-                break;
+        do {
+            $sock = @fsockopen('localhost', $port, $errno, $errstr, 10);
+            if ($sock === false) {
+                // set a 5 secs timeout waiting for the server to start
+                if (microtime(true) > $start + 5) {
+                    $this->pid = 0; // signal failure
+                    break;
+                }
             }
-        }
+        } while ($sock === false);
+
         if ($sock !== false) {
             fclose($sock);
         }
@@ -199,6 +202,7 @@ class BuiltInServer
             CURLOPT_HEADER => 1,
         ]);
         curl_setopt_array($ch, $curlopts);
+        /** @var mixed $resp */
         $resp = curl_exec($ch);
         $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
         list($header, $body) = explode("\r\n\r\n", $resp, 2);
diff --git a/tests/SigningTestCase.php b/tests/SigningTestCase.php
index 5bf95a4566b53df230db70e2605806d9066c4933..c561fa598aa36f3fedda4e6ab3005581cb09794f 100644
--- a/tests/SigningTestCase.php
+++ b/tests/SigningTestCase.php
@@ -15,6 +15,7 @@ use \org\bovigo\vfs\vfsStream;
 class SigningTestCase extends TestCase
 {
     // openssl genrsa -out ca.key.pem 2048
+    /** @var string $ca_private_key */
     protected $ca_private_key = <<<'NOWDOC'
 -----BEGIN RSA PRIVATE KEY-----
 MIIEpQIBAAKCAQEAtj5GuvnC5aCg8bhq2Yy4isp/uXtRRWKhbB5aYP7/1DwwwQ1Z
@@ -46,6 +47,7 @@ mHreqJFXp12lURaL+esz01oaH49ZUzVeZVGmVyOzoSDYEOq9K7L/j14=
 NOWDOC;
 
     // openssl req -key ca.key.pem -new -x509 -days 3650 -out ca.cert.pem
+    /** @var string $ca_certificate */
     private $ca_certificate = <<<'NOWDOC'
 -----BEGIN CERTIFICATE-----
 MIIDtjCCAp6gAwIBAgIJAII4rW68Q+IsMA0GCSqGSIb3DQEBCwUAMHAxCzAJBgNV
@@ -72,6 +74,7 @@ BdyrA5DmvSuL/Yfq03J9btXX4NnANQFVvfSbun7ts5F1qTkSe/vHCoke
 NOWDOC;
 
     // openssl genrsa -out good.key.pem 2048
+    /** @var string $good_private_key */
     protected $good_private_key = <<<'NOWDOC'
 -----BEGIN RSA PRIVATE KEY-----
 MIIEowIBAAKCAQEAqmNn4bt/jrMHgoWtwXLc2ok17BHh1O5ETbn9rK3KFjk3BXp5
@@ -111,6 +114,7 @@ NOWDOC;
     //      -days 3650 \
     //      -in good.csr.pem \
     //      -out good.cert.pem
+    /** @var string $good_certificate */
     protected $good_certificate = <<<'NOWDOC'
 -----BEGIN CERTIFICATE-----
 MIIDZTCCAk0CCQC+sxqJmyko6TANBgkqhkiG9w0BAQsFADBwMQswCQYDVQQGEwJB
@@ -135,6 +139,30 @@ hQc0xnrLQ255SjMn+nQtMkVSuKwAUqaAP1ByyiVbN1cBlHnMiJCjvBI58bSTdlVK
 -----END CERTIFICATE-----
 NOWDOC;
 
+    /** @var string */
+    protected $good_private_key_file;
+
+    /** @var string */
+    protected $good_certificate_file;
+
+    /** @var string */
+    protected $certdir;
+
+    /** @var \org\bovigo\vfs\vfsStreamDirectory */
+    protected $root;
+
+    /** @var string */
+    protected $root_directory;
+
+    /** @var string */
+    protected $ca_private_key_file;
+
+    /** @var string */
+    protected $ca_certificate_file;
+
+    /** @var \SimpleSAML\Configuration */
+    protected $config;
+
     const ROOTDIRNAME = 'testdir';
     const DEFAULTCERTDIR = 'certdir';
     const CA_PRIVATE_KEY = 'ca.key.pem';
diff --git a/tests/Utils/ReduceSpillOverTest.php b/tests/Utils/ReduceSpillOverTest.php
index c29360b7b4938fb60ad4eb9c129ab22e22c2423a..c53ba6f7ddcfa0feaf6937d293395b2d9e470038 100644
--- a/tests/Utils/ReduceSpillOverTest.php
+++ b/tests/Utils/ReduceSpillOverTest.php
@@ -28,9 +28,10 @@ class ReduceSpillOverTest extends ClearStateTestCase
      */
     public function testStateRemoved()
     {
-
         $this->assertArrayNotHasKey('QUERY_STRING', $_SERVER);
-        $this->assertFalse(getenv('SIMPLESAMLPHP_CONFIG_DIR'));
+        /** @var false $env */
+        $env = getenv('SIMPLESAMLPHP_CONFIG_DIR');
+        $this->assertFalse($env);
         try {
             \SimpleSAML\Configuration::getInstance();
             $this->fail('Expected config configured in other tests to no longer be valid');
diff --git a/tests/lib/SimpleSAML/DatabaseTest.php b/tests/lib/SimpleSAML/DatabaseTest.php
index 6ba5c9bf6a577c526dc6a2b23ca7cb90dccacb55..f30db8ed49d928156f034356a62d3c3068b16ed9 100644
--- a/tests/lib/SimpleSAML/DatabaseTest.php
+++ b/tests/lib/SimpleSAML/DatabaseTest.php
@@ -245,21 +245,22 @@ class DatabaseTest extends TestCase
         $this->assertEquals($this->config->getString('database.prefix')."sspdbt", $table);
 
         $this->db->write(
-            "CREATE TABLE IF NOT EXISTS $table (ssp_key INT(16) NOT NULL, ssp_value TEXT NOT NULL)",
-            false
+            "CREATE TABLE IF NOT EXISTS $table (ssp_key INT(16) NOT NULL, ssp_value TEXT NOT NULL)"
         );
 
+        /** @var \PDOStatement $query1 */
         $query1 = $this->db->read("SELECT * FROM $table");
         $this->assertEquals(0, $query1->fetch(), "Table $table is not empty when it should be.");
 
         $ssp_key = time();
-        $ssp_value = md5(rand(0, 10000));
+        $ssp_value = md5(strval(rand(0, 10000)));
         $stmt = $this->db->write(
             "INSERT INTO $table (ssp_key, ssp_value) VALUES (:ssp_key, :ssp_value)",
             ['ssp_key' => [$ssp_key, \PDO::PARAM_INT], 'ssp_value' => $ssp_value]
         );
         $this->assertEquals(1, $stmt, "Could not insert data into $table.");
 
+        /** @var \PDOStatement $query2 */
         $query2 = $this->db->read("SELECT * FROM $table WHERE ssp_key = :ssp_key", ['ssp_key' => $ssp_key]);
         $data = $query2->fetch();
         $this->assertEquals($data['ssp_value'], $ssp_value, "Inserted data doesn't match what is in the database");
@@ -291,7 +292,7 @@ class DatabaseTest extends TestCase
     public function noSuchTable()
     {
         $this->expectException(\Exception::class);
-        $this->db->write("DROP TABLE phpunit_nonexistent", false);
+        $this->db->write("DROP TABLE phpunit_nonexistent");
     }
 
 
@@ -301,7 +302,7 @@ class DatabaseTest extends TestCase
     public function tearDown()
     {
         $table = $this->db->applyPrefix("sspdbt");
-        $this->db->write("DROP TABLE IF EXISTS $table", false);
+        $this->db->write("DROP TABLE IF EXISTS $table");
 
         unset($this->config);
         unset($this->db);
diff --git a/tests/lib/SimpleSAML/Metadata/SAMLBuilderTest.php b/tests/lib/SimpleSAML/Metadata/SAMLBuilderTest.php
index 04d471791b254c4c025ea322fcbd79463d1c9a6c..7d30d2161dba13016a36601c9094e5c0934e64e5 100644
--- a/tests/lib/SimpleSAML/Metadata/SAMLBuilderTest.php
+++ b/tests/lib/SimpleSAML/Metadata/SAMLBuilderTest.php
@@ -37,6 +37,7 @@ class SAMLBuilderTest extends TestCase
         $samlBuilder->addMetadata($set, $metadata);
 
         $spDesc = $samlBuilder->getEntityDescriptor();
+        /** @var \DOMNodeList $acs */
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
         $this->assertEquals(1, $acs->length);
         $attributes = $acs->item(0)->getElementsByTagName("RequestedAttribute");
@@ -66,6 +67,7 @@ class SAMLBuilderTest extends TestCase
         $samlBuilder->addMetadata($set, $metadata);
 
         $spDesc = $samlBuilder->getEntityDescriptor();
+        /** @var \DOMNodeList $acs */
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
         $this->assertEquals(1, $acs->length);
         $attributes = $acs->item(0)->getElementsByTagName("RequestedAttribute");
@@ -97,6 +99,7 @@ class SAMLBuilderTest extends TestCase
         $samlBuilder->addMetadata($set, $metadata);
 
         $spDesc = $samlBuilder->getEntityDescriptor();
+        /** @var \DOMNodeList $acs */
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
         $this->assertEquals(1, $acs->length);
         $attributes = $acs->item(0)->getElementsByTagName("RequestedAttribute");
@@ -126,6 +129,7 @@ class SAMLBuilderTest extends TestCase
         $samlBuilder->addMetadata($set, $metadata);
 
         $spDesc = $samlBuilder->getEntityDescriptor();
+        /** @var \DOMNodeList $acs */
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
         $this->assertEquals(1, $acs->length);
         $attributes = $acs->item(0)->getElementsByTagName("RequestedAttribute");
@@ -164,6 +168,7 @@ class SAMLBuilderTest extends TestCase
         $samlBuilder->addMetadata($set, $metadata);
 
         $spDesc = $samlBuilder->getEntityDescriptor();
+        /** @var \DOMNodeList $acs */
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
         $acs1 = $acs->item(0);
         $this->assertFalse($acs1->hasAttribute("isDefault"));
@@ -174,6 +179,7 @@ class SAMLBuilderTest extends TestCase
         $samlBuilder->addMetadata($set, $metadata);
         $spDesc = $samlBuilder->getEntityDescriptor();
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
+        /** @var \DOMElement $acs1 */
         $acs1 = $acs->item(0);
         $this->assertTrue($acs1->hasAttribute("isDefault"));
         $this->assertEquals("true", $acs1->getAttribute("isDefault"));
@@ -184,6 +190,7 @@ class SAMLBuilderTest extends TestCase
         $samlBuilder->addMetadata($set, $metadata);
         $spDesc = $samlBuilder->getEntityDescriptor();
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
+        /** @var \DOMElement $acs1 */
         $acs1 = $acs->item(0);
         $this->assertTrue($acs1->hasAttribute("isDefault"));
         $this->assertEquals("false", $acs1->getAttribute("isDefault"));
@@ -214,6 +221,7 @@ class SAMLBuilderTest extends TestCase
 
         $spDesc = $samlBuilder->getEntityDescriptor();
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
+        /** @var \DOMElement $acs1 */
         $acs1 = $acs->item(0);
         $this->assertTrue($acs1->hasAttribute("index"));
         $this->assertEquals("0", $acs1->getAttribute("index"));
@@ -225,6 +233,7 @@ class SAMLBuilderTest extends TestCase
 
         $spDesc = $samlBuilder->getEntityDescriptor();
         $acs = $spDesc->getElementsByTagName("AttributeConsumingService");
+        /** @var \DOMElement $acs1 */
         $acs1 = $acs->item(0);
         $this->assertTrue($acs1->hasAttribute("index"));
         $this->assertEquals("15", $acs1->getAttribute("index"));
diff --git a/tests/lib/SimpleSAML/Metadata/SAMLParserTest.php b/tests/lib/SimpleSAML/Metadata/SAMLParserTest.php
index 78e15b5a23a1789deb878adbb404999f4117304b..79f5120c3862893866a768452adc62a6a17ceb8a 100644
--- a/tests/lib/SimpleSAML/Metadata/SAMLParserTest.php
+++ b/tests/lib/SimpleSAML/Metadata/SAMLParserTest.php
@@ -41,6 +41,7 @@ XML
         $entities = \SimpleSAML\Metadata\SAMLParser::parseDescriptorsElement($document->documentElement);
         $this->assertArrayHasKey('theEntityID', $entities);
         // RegistrationInfo is accessible in the SP or IDP metadata accessors
+        /** @var array $metadata */
         $metadata = $entities['theEntityID']->getMetadata20SP();
         $this->assertEquals($expected, $metadata['RegistrationInfo']);
     }
@@ -85,12 +86,15 @@ XML
         $this->assertArrayHasKey('theEntityID', $entities);
         $this->assertArrayHasKey('subEntityId', $entities);
         // RegistrationInfo is accessible in the SP or IDP metadata accessors
+        /** @var array $metadata */
         $metadata = $entities['theEntityID']->getMetadata20SP();
         $this->assertEquals($expected, $metadata['RegistrationInfo']);
 
+        /** @var array $metadata */
         $metadata = $entities['subEntityId']->getMetadata20SP();
         $this->assertEquals($expected, $metadata['RegistrationInfo']);
 
+        /** @var array $metadata */
         $metadata = $entities['subEntityIdOverride']->getMetadata20SP();
         $this->assertEquals($expected, $metadata['RegistrationInfo']);
     }
@@ -128,6 +132,7 @@ XML
         $entities = \SimpleSAML\Metadata\SAMLParser::parseDescriptorsElement($document->documentElement);
         $this->assertArrayHasKey('theEntityID', $entities);
 
+        /** @var array $metadata */
         $metadata = $entities['theEntityID']->getMetadata20SP();
 
         $this->assertEquals("Example service", $metadata['name']['en']);
@@ -338,6 +343,7 @@ XML
         $entities = \SimpleSAML\Metadata\SAMLParser::parseDescriptorsElement($document->documentElement);
         $this->assertArrayHasKey('theEntityID', $entities);
         // Various MDUI elements are accessible
+        /** @var array $metadata */
         $metadata = $entities['theEntityID']->getMetadata20IdP();
         $this->assertEquals($expected['scope'], $metadata['scope'], 'shibmd:Scope elements not reflected in parsed metadata');
         $this->assertEquals($expected['UIInfo'], $metadata['UIInfo'], 'mdui:UIInfo elements not reflected in parsed metadata');
diff --git a/tests/lib/SimpleSAML/Metadata/test-metadata/source1/saml20-sp-remote.php b/tests/lib/SimpleSAML/Metadata/test-metadata/source1/saml20-sp-remote.php
index e6de7efd5f1d7afb0da9cd71d3d155675d8a3c29..5be0b25f292ebfd2fff58eaa8ff188608f17da9e 100644
--- a/tests/lib/SimpleSAML/Metadata/test-metadata/source1/saml20-sp-remote.php
+++ b/tests/lib/SimpleSAML/Metadata/test-metadata/source1/saml20-sp-remote.php
@@ -1,5 +1,7 @@
 <?php
 
+$metadata = [];
+
 $metadata['entityA'] = [
     'entityid' => 'entityA',
     'name' =>
diff --git a/tests/lib/SimpleSAML/SessionHandlerPHPTest.php b/tests/lib/SimpleSAML/SessionHandlerPHPTest.php
index 7e1e720fd2b59af2935c90ff0217952c1e41e7f3..43bc2bef11aa6f6a11feff5f8707aaa27af0ca40 100644
--- a/tests/lib/SimpleSAML/SessionHandlerPHPTest.php
+++ b/tests/lib/SimpleSAML/SessionHandlerPHPTest.php
@@ -9,6 +9,7 @@ use SimpleSAML\Configuration;
 
 class SessionHandlerPHPTest extends ClearStateTestCase
 {
+    /** @var array */
     protected $sessionConfig = [
         'session.cookie.name' => 'SimpleSAMLSessionID',
         'session.cookie.lifetime' => 100,
@@ -17,8 +18,14 @@ class SessionHandlerPHPTest extends ClearStateTestCase
         'session.cookie.secure' => true,
         'session.phpsession.cookiename' => 'SimpleSAML',
     ];
+
+    /** @var array */
     protected $original;
 
+
+    /**
+     * @return void
+     */
     protected function setUp()
     {
         $this->original = $_SERVER;
@@ -29,15 +36,21 @@ class SessionHandlerPHPTest extends ClearStateTestCase
         $_SERVER['REQUEST_URI'] = '/simplesaml';
     }
 
+
+    /**
+     * @return void
+     */
     protected function tearDown()
     {
         $_SERVER = $this->original;
     }
 
+
     /**
      * @covers SimpleSAML\SessionHandlerPHP::__construct()
      * @covers SimpleSAML\SessionHandlerPHP::getSessionHandler()
      * @covers SimpleSAML\SessionHandler::getSessionHandler()
+     * @return void
      */
     public function testGetSessionHandler()
     {
@@ -46,16 +59,18 @@ class SessionHandlerPHPTest extends ClearStateTestCase
         $this->assertInstanceOf('\SimpleSAML\SessionHandlerPHP', $sh);
     }
 
+
     /**
      * @covers SimpleSAML\SessionHandlerPHP::setCookie()
      * @runInSeparateProcess
      * @requires extension xdebug
+     * @return void
      */
     public function testSetCookie()
     {
         Configuration::loadFromArray($this->sessionConfig, '[ARRAY]', 'simplesaml');
         $sh = SessionHandlerPHP::getSessionHandler();
-        $sh->setCookie('SimpleSAMLSessionID', 1);
+        $sh->setCookie('SimpleSAMLSessionID', '1');
 
         $headers = xdebug_get_headers();
         $this->assertContains('SimpleSAML=1;', $headers[0]);
@@ -66,14 +81,20 @@ class SessionHandlerPHPTest extends ClearStateTestCase
         $this->assertRegExp('/\b[Hh]ttp[Oo]nly(;|$)/', $headers[0]);
     }
 
+
     /**
      * @covers SimpleSAML\SessionHandlerPHP::setCookie()
      * @runInSeparateProcess
      * @requires extension xdebug
+     * @return void
      */
     public function testSetCookieSameSiteNone()
     {
-        Configuration::loadFromArray(array_merge($this->sessionConfig, ['session.cookie.samesite' => 'None']), '[ARRAY]', 'simplesaml');
+        Configuration::loadFromArray(
+            array_merge($this->sessionConfig, ['session.cookie.samesite' => 'None']),
+            '[ARRAY]',
+            'simplesaml'
+        );
         $sh = SessionHandlerPHP::getSessionHandler();
         $sh->setCookie('SimpleSAMLSessionID', 'None');
 
@@ -82,14 +103,20 @@ class SessionHandlerPHPTest extends ClearStateTestCase
         $this->assertRegExp('/\b[Ss]ame[Ss]ite=None(;|$)/', $headers[0]);
     }
 
+
     /**
      * @covers SimpleSAML\SessionHandlerPHP::setCookie()
      * @runInSeparateProcess
      * @requires extension xdebug
+     * @return void
      */
     public function testSetCookieSameSiteLax()
     {
-        Configuration::loadFromArray(array_merge($this->sessionConfig, ['session.cookie.samesite' => 'Lax']), '[ARRAY]', 'simplesaml');
+        Configuration::loadFromArray(
+            array_merge($this->sessionConfig, ['session.cookie.samesite' => 'Lax']),
+            '[ARRAY]',
+            'simplesaml'
+        );
         $sh = SessionHandlerPHP::getSessionHandler();
         $sh->setCookie('SimpleSAMLSessionID', 'Lax');
 
@@ -98,14 +125,20 @@ class SessionHandlerPHPTest extends ClearStateTestCase
         $this->assertRegExp('/\b[Ss]ame[Ss]ite=Lax(;|$)/', $headers[0]);
     }
 
+
     /**
      * @covers SimpleSAML\SessionHandlerPHP::setCookie()
      * @runInSeparateProcess
      * @requires extension xdebug
+     * @return void
      */
     public function testSetCookieSameSiteStrict()
     {
-        Configuration::loadFromArray(array_merge($this->sessionConfig, ['session.cookie.samesite' => 'Strict']), '[ARRAY]', 'simplesaml');
+        Configuration::loadFromArray(
+            array_merge($this->sessionConfig, ['session.cookie.samesite' => 'Strict']),
+            '[ARRAY]',
+            'simplesaml'
+        );
         $sh = SessionHandlerPHP::getSessionHandler();
         $sh->setCookie('SimpleSAMLSessionID', 'Strict');
 
@@ -114,10 +147,12 @@ class SessionHandlerPHPTest extends ClearStateTestCase
         $this->assertRegExp('/\b[Ss]ame[Ss]ite=Strict(;|$)/', $headers[0]);
     }
 
+
     /**
      * @covers SimpleSAML\SessionHandlerPHP::restorePrevious()
      * @runInSeparateProcess
      * @requires extension xdebug
+     * @return void
      */
     public function testRestorePrevious()
     {
@@ -126,9 +161,10 @@ class SessionHandlerPHPTest extends ClearStateTestCase
         session_start();
 
         Configuration::loadFromArray($this->sessionConfig, '[ARRAY]', 'simplesaml');
+        /** @var SessionHandlerPHP $sh */
         $sh = SessionHandlerPHP::getSessionHandler();
         $sh->setCookie('SimpleSAMLSessionID', 'Restore');
-        $oldSh = $sh->restorePrevious();
+        $sh->restorePrevious();
 
         $headers = xdebug_get_headers();
         $this->assertContains('PHPSESSID='.$sid, $headers[0]);
@@ -137,8 +173,10 @@ class SessionHandlerPHPTest extends ClearStateTestCase
         $this->assertEquals($headers[0], $headers[2]);
     }
 
+
     /**
      * @covers SimpleSAML\SessionHandlerPHP::newSessionId()
+     * @return void
      */
     public function testNewSessionId()
     {
diff --git a/tests/lib/SimpleSAML/Store/RedisTest.php b/tests/lib/SimpleSAML/Store/RedisTest.php
index db6f31cdd1d309b8810907714771f51130222ba9..3f5593360bed8f0fbc4f16c740a2205456352067 100644
--- a/tests/lib/SimpleSAML/Store/RedisTest.php
+++ b/tests/lib/SimpleSAML/Store/RedisTest.php
@@ -16,6 +16,16 @@ use SimpleSAML\Store;
  */
 class RedisTest extends TestCase
 {
+    /** @var \PHPUnit_Framework_MockObject_MockObject */
+    protected $mocked_redis;
+
+    /** @var \SimpleSAML\Store\Redis */
+    protected $redis;
+
+    /** @var array */
+    protected $config;
+
+
     /**
      * @return void
      */
@@ -28,25 +38,31 @@ class RedisTest extends TestCase
                                    ->disableOriginalConstructor()
                                    ->getMock();
 
+        /** @psalm-suppress UndefinedMethod   Remove when Psalm 3.x is in place */
         $this->mocked_redis->method('get')
                            ->will($this->returnCallback([$this, 'getMocked']));
 
+        /** @psalm-suppress UndefinedMethod   Remove when Psalm 3.x is in place */
         $this->mocked_redis->method('set')
                            ->will($this->returnCallback([$this, 'setMocked']));
 
+        /** @psalm-suppress UndefinedMethod   Remove when Psalm 3.x is in place */
         $this->mocked_redis->method('setex')
                            ->will($this->returnCallback([$this, 'setexMocked']));
 
+        /** @psalm-suppress UndefinedMethod   Remove when Psalm 3.x is in place */
         $this->mocked_redis->method('del')
                            ->will($this->returnCallback([$this, 'delMocked']));
 
-        $nop = function () {
+        $nop = /** @return void */ function () {
             return;
         };
 
+        /** @psalm-suppress UndefinedMethod   Remove when Psalm 3.x is in place */
         $this->mocked_redis->method('disconnect')
                            ->will($this->returnCallback($nop));
 
+        /** @var \Predis\Client $this->mocked_redis */
         $this->redis = new Store\Redis($this->mocked_redis);
     }
 
@@ -108,6 +124,7 @@ class RedisTest extends TestCase
             'store.redis.prefix' => 'phpunit_',
         ], '[ARRAY]', 'simplesaml');
 
+        /** @var \SimpleSAML\Store\Redis $store */
         $store = Store::getInstance();
 
         $this->assertInstanceOf('SimpleSAML\Store\Redis', $store);
@@ -131,6 +148,7 @@ class RedisTest extends TestCase
             'store.redis.password' => 'password',
         ], '[ARRAY]', 'simplesaml');
 
+        /** @var \SimpleSAML\Store\Redis $store */
         $store = Store::getInstance();
 
         $this->assertInstanceOf('SimpleSAML\Store\Redis', $store);
diff --git a/tests/lib/SimpleSAML/Store/SQLTest.php b/tests/lib/SimpleSAML/Store/SQLTest.php
index bff22681a521683e309f737ff01830f7e9d7c937..46188863323c4a01d92e80b8cbc8818779cd95ee 100644
--- a/tests/lib/SimpleSAML/Store/SQLTest.php
+++ b/tests/lib/SimpleSAML/Store/SQLTest.php
@@ -199,6 +199,7 @@ class SQLTest extends TestCase
     protected function tearDown()
     {
         $config = Configuration::getInstance();
+        /** @var \SimpleSAML\Store\SQL $store */
         $store = Store::getInstance();
 
         $this->clearInstance($config, '\SimpleSAML\Configuration');
diff --git a/tests/lib/SimpleSAML/StoreTest.php b/tests/lib/SimpleSAML/StoreTest.php
index 756a53382cf0785d00d99395c28f3cacc1729341..92e4b212cb2a55866bcdf61c41454a9916e5c237 100644
--- a/tests/lib/SimpleSAML/StoreTest.php
+++ b/tests/lib/SimpleSAML/StoreTest.php
@@ -29,6 +29,7 @@ class StoreTest extends TestCase
         Configuration::loadFromArray([
         ], '[ARRAY]', 'simplesaml');
 
+        /** @var false $store */
         $store = Store::getInstance();
 
         $this->assertFalse($store);
@@ -45,6 +46,7 @@ class StoreTest extends TestCase
         Configuration::loadFromArray([
         ], '[ARRAY]', 'simplesaml');
 
+        /** @var false $store */
         $store = Store::getInstance();
 
         $this->assertFalse($store);
@@ -130,6 +132,7 @@ class StoreTest extends TestCase
     protected function tearDown()
     {
         $config = Configuration::getInstance();
+        /** @var \SimpleSAML\Store $store */
         $store = Store::getInstance();
 
         $this->clearInstance($config, '\SimpleSAML\Configuration');
diff --git a/tests/lib/SimpleSAML/Utils/ArraysTest.php b/tests/lib/SimpleSAML/Utils/ArraysTest.php
index 9d103d32024c69ccbe79df271894f6d1102a5547..bf8b6d60ce7ba3091e71fe255aea1d7b5725f0a1 100644
--- a/tests/lib/SimpleSAML/Utils/ArraysTest.php
+++ b/tests/lib/SimpleSAML/Utils/ArraysTest.php
@@ -45,6 +45,7 @@ class ArraysTest extends TestCase
     public function testTranspose()
     {
         // check not array
+        /** @psalm-suppress InvalidArgument   Can be removed as soon as the codebase is fully typehinted */
         $this->assertFalse(Arrays::transpose('string'));
 
         // check bad arrays
diff --git a/tests/lib/SimpleSAML/Utils/AttributesTest.php b/tests/lib/SimpleSAML/Utils/AttributesTest.php
index 5b6b33336474944ab8432cc93e0f85511b9dea28..3ca7c1f04d3d67dda78da848f1ee6f706c2239c8 100644
--- a/tests/lib/SimpleSAML/Utils/AttributesTest.php
+++ b/tests/lib/SimpleSAML/Utils/AttributesTest.php
@@ -16,6 +16,8 @@ class AttributesTest extends TestCase
     /**
      * Test the getExpectedAttribute() method with invalid attributes array.
      * @return void
+     * @psalm-suppress InvalidArgument
+     * @deprecated Can be removed as soon as the codebase is fully typehinted
      */
     public function testGetExpectedAttributeInvalidAttributesArray()
     {
@@ -32,6 +34,8 @@ class AttributesTest extends TestCase
 
     /**
      * Test the getExpectedAttributeMethod() method with invalid expected attribute parameter.
+     * @deprecated Remove this test as soon as the codebase is fully typehinted
+     * @psalm-suppress PossiblyFalseArgument
      * @return void
      */
     public function testGetExpectedAttributeInvalidAttributeName()
@@ -149,6 +153,8 @@ class AttributesTest extends TestCase
     /**
      * Test the normalizeAttributesArray() function with input not being an array
      * @return void
+     * @psalm-suppress InvalidArgument
+     * @deprecated Can be removed as soon as the codebase is fully typehinted
      */
     public function testNormalizeAttributesArrayBadInput()
     {
diff --git a/tests/lib/SimpleSAML/Utils/Config/MetadataTest.php b/tests/lib/SimpleSAML/Utils/Config/MetadataTest.php
index 74e9c217cc98e7c297b117adbf730db384bbb0e9..78e51aefb43b2bd5134dca6ed0339ea3fddaf714 100644
--- a/tests/lib/SimpleSAML/Utils/Config/MetadataTest.php
+++ b/tests/lib/SimpleSAML/Utils/Config/MetadataTest.php
@@ -18,6 +18,7 @@ class MetadataTest extends TestCase
     {
         // test invalid argument
         try {
+            /** @psalm-suppress InvalidArgument   May be removed in 2.0 when codebase is fully typehinted */
             Metadata::getContact('string');
         } catch (\InvalidArgumentException $e) {
             $this->assertEquals('Invalid input parameters', $e->getMessage());
diff --git a/tests/lib/SimpleSAML/Utils/CryptoTest.php b/tests/lib/SimpleSAML/Utils/CryptoTest.php
index 45e7a3502fa80bd16d6fd4c5cf77e40decf193b5..e0f38cb12b24c50a21111e04fa9ff9828779d147 100644
--- a/tests/lib/SimpleSAML/Utils/CryptoTest.php
+++ b/tests/lib/SimpleSAML/Utils/CryptoTest.php
@@ -3,8 +3,8 @@
 namespace SimpleSAML\Test\Utils;
 
 use PHPUnit\Framework\TestCase;
+use SimpleSAML\Configuration;
 use SimpleSAML\Utils\Crypto;
-use \SimpleSAML\Configuration;
 
 use \org\bovigo\vfs\vfsStream;
 
@@ -17,6 +17,15 @@ class CryptoTest extends TestCase
 
     const DEFAULTCERTDIR = 'certdir';
 
+    /** @var \org\bovigo\vfs\vfsStreamDirectory */
+    protected $root;
+
+    /** @var string */
+    protected $root_directory;
+
+    /** @var string */
+    protected $certdir;
+
 
     /**
      * @return void
@@ -621,6 +630,7 @@ PHP;
             'test'
         );
 
+        /** @var array $pubkey */
         $pubkey = Crypto::loadPublicKey($config);
         $res = $pubkey['certData'];
         $expected = $x509certificate;
diff --git a/tests/lib/SimpleSAML/Utils/HTTPTest.php b/tests/lib/SimpleSAML/Utils/HTTPTest.php
index b5a1a4f18b5046d1d54c63dfc561fe7e42d2f5a4..7e4683a079361fad7655f82bd4e50d32144eb641 100644
--- a/tests/lib/SimpleSAML/Utils/HTTPTest.php
+++ b/tests/lib/SimpleSAML/Utils/HTTPTest.php
@@ -38,6 +38,8 @@ class HTTPTest extends ClearStateTestCase
     /**
      * Test SimpleSAML\Utils\HTTP::addURLParameters().
      * @return void
+     * @psalm-suppress InvalidArgument
+     * @deprecated Can be removed in 2.0 when codebase if fully typehinted
      */
     public function testAddURLParametersInvalidURL()
     {
@@ -49,6 +51,8 @@ class HTTPTest extends ClearStateTestCase
     /**
      * Test SimpleSAML\Utils\HTTP::addURLParameters().
      * @return void
+     * @psalm-suppress InvalidArgument
+     * @deprecated Can be removed in 2.0 when codebase if fully typehinted
      */
     public function testAddURLParametersInvalidParameters()
     {
@@ -457,6 +461,7 @@ class HTTPTest extends ClearStateTestCase
      * @covers SimpleSAML\Utils\HTTP::setCookie()
      * @runInSeparateProcess
      * @requires extension xdebug
+     * @return void
      */
     public function testSetCookie()
     {
@@ -490,6 +495,7 @@ class HTTPTest extends ClearStateTestCase
 
     /**
      * @covers SimpleSAML\Utils\HTTP::setCookie()
+     * @return void
      */
     public function testSetCookieInsecure()
     {
@@ -511,6 +517,7 @@ class HTTPTest extends ClearStateTestCase
      * @covers SimpleSAML\Utils\HTTP::setCookie()
      * @runInSeparateProcess
      * @requires extension xdebug
+     * @return void
      */
     public function testSetCookieSameSite()
     {
diff --git a/tests/lib/SimpleSAML/Utils/SystemTest.php b/tests/lib/SimpleSAML/Utils/SystemTest.php
index 9f73b88fd45a2804a87902c4abca85d587b3c817..92ac6f6e6c0074026deaad7455be3ae11d6e5f1e 100644
--- a/tests/lib/SimpleSAML/Utils/SystemTest.php
+++ b/tests/lib/SimpleSAML/Utils/SystemTest.php
@@ -17,6 +17,12 @@ class SystemTest extends TestCase
 
     const DEFAULTTEMPDIR = 'tempdir';
 
+    /** @var \org\bovigo\vfs\vfsStreamDirectory */
+    protected $root;
+
+    /** @var string */
+    protected $root_directory;
+
 
     /**
      * @return void
diff --git a/tests/lib/SimpleSAML/Utils/TimeTest.php b/tests/lib/SimpleSAML/Utils/TimeTest.php
index 157ff080b3d727e632a6919d68857dea66b2fcf9..52ea3bd047b419abb8eb77e325589cf794aec9e6 100644
--- a/tests/lib/SimpleSAML/Utils/TimeTest.php
+++ b/tests/lib/SimpleSAML/Utils/TimeTest.php
@@ -139,6 +139,10 @@ class TimeTest extends TestCase
         // test invalid input parameters
         try {
             // invalid duration
+            /**
+             * @deprecated This test becomes useless as soon as the codebase is fully typehinted
+             * @psalm-suppress InvalidScalarArgument
+             */
             Time::parseDuration(0);
             $this->fail("Did not fail with invalid duration parameter.");
         } catch (\InvalidArgumentException $e) {
@@ -146,6 +150,10 @@ class TimeTest extends TestCase
         }
         try {
             // invalid timestamp
+            /**
+             * @deprecated This test becomes useless as soon as the codebase is fully typehinted
+             * @psalm-suppress InvalidArgument
+             */
             Time::parseDuration('', []);
             $this->fail("Did not fail with invalid timestamp parameter.");
         } catch (\InvalidArgumentException $e) {
diff --git a/tests/lib/SimpleSAML/Utils/XMLTest.php b/tests/lib/SimpleSAML/Utils/XMLTest.php
index 55fda70bac4d54666b9972d4c1d09533c7bf3387..bac2945866e30bf401cdfe230bab49d2f7e3bd25 100644
--- a/tests/lib/SimpleSAML/Utils/XMLTest.php
+++ b/tests/lib/SimpleSAML/Utils/XMLTest.php
@@ -426,6 +426,7 @@ XMLDOC;
 
         $dom = new \DOMDocument('1.0');
         $dom->loadXML($xml, LIBXML_NONET);
+
         $res = XML::isValid($dom, $schema);
         $this->assertTrue($res);
     }
diff --git a/tests/lib/SimpleSAML/XML/ErrorsTest.php b/tests/lib/SimpleSAML/XML/ErrorsTest.php
index c671a23e1190c23dff05db877d3fd284eec1d733..fadc0ebc3a648c63f3cdb7e8e62c21b53a2d9b01 100644
--- a/tests/lib/SimpleSAML/XML/ErrorsTest.php
+++ b/tests/lib/SimpleSAML/XML/ErrorsTest.php
@@ -47,16 +47,16 @@ class ErrorsTest extends TestCase
     public function formatErrors()
     {
         $error = new \LibXMLError();
-        $error->level = 'level';
-        $error->code = 'code';
-        $error->line = 'line';
-        $error->column = 'col';
+        $error->level = 3;
+        $error->code = 76;
+        $error->line = 1;
+        $error->column = 18;
         $error->message = ' msg ';
 
         $errors = Errors::formatErrors([$error, $error]);
 
         $this->assertEquals(
-            "level=level,code=code,line=line,col=col,msg=msg\nlevel=level,code=code,line=line,col=col,msg=msg\n",
+            "level=3,code=76,line=1,col=18,msg=msg\nlevel=3,code=76,line=1,col=18,msg=msg\n",
             $errors
         );
     }
diff --git a/tests/lib/SimpleSAML/XML/SignerTest.php b/tests/lib/SimpleSAML/XML/SignerTest.php
index 82b56876cde759f5f8787516d17ffcacc17298ac..32e8177bc0fd4bd6e41e1671c0e932f1dda7de03 100644
--- a/tests/lib/SimpleSAML/XML/SignerTest.php
+++ b/tests/lib/SimpleSAML/XML/SignerTest.php
@@ -16,7 +16,11 @@ use \org\bovigo\vfs\vfsStream;
  */
 class SignerTest extends SigningTestCase
 {
+    /** @var string */
+    private $other_certificate_file;
+
     // openssl req -new -x509 -key good.key.pem -out public2.pem -days 3650
+    /** @var string */
     private $other_certificate = <<<'NOWDOC'
 -----BEGIN CERTIFICATE-----
 MIIDazCCAlOgAwIBAgIUGPKUWW1GN07xxAsGENQ+rZPyABAwDQYJKoZIhvcNAQEL
diff --git a/tests/modules/core/lib/Auth/Process/AttributeCopyTest.php b/tests/modules/core/lib/Auth/Process/AttributeCopyTest.php
index a1a70d08803fcb51d6b134e7b75a1078c0a5f42b..1b1abf9f7848868e8be063e64548aac244b27dc0 100644
--- a/tests/modules/core/lib/Auth/Process/AttributeCopyTest.php
+++ b/tests/modules/core/lib/Auth/Process/AttributeCopyTest.php
@@ -9,7 +9,6 @@ use PHPUnit\Framework\TestCase;
  */
 class AttributeCopyTest extends TestCase
 {
-
     /**
      * Helper function to run the filter with a given configuration.
      *
diff --git a/tests/modules/core/lib/Auth/Process/AttributeRealmTest.php b/tests/modules/core/lib/Auth/Process/AttributeRealmTest.php
index 4b49708d61b402a5e3e5eed6047f1aea48f53e5c..a89a73aa301cb44fd27c23399a5024b5230595cf 100644
--- a/tests/modules/core/lib/Auth/Process/AttributeRealmTest.php
+++ b/tests/modules/core/lib/Auth/Process/AttributeRealmTest.php
@@ -6,6 +6,7 @@ use PHPUnit\Framework\TestCase;
 
 /**
  * Test for the core:AttributeRealm filter.
+ * @deprecated Remove in 2.0
  */
 class AttributeRealmTest extends TestCase
 {
@@ -23,6 +24,7 @@ class AttributeRealmTest extends TestCase
         return $request;
     }
 
+
     /**
      * Test the most basic functionality.
      * @return void
diff --git a/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php b/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php
index f41317ef2f6f31da85c71d2e1d3943e42762caa5..53cc8a104894c412887e4c0d2583b92910a0b3ba 100644
--- a/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php
+++ b/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php
@@ -7,6 +7,7 @@ namespace SimpleSAML\Test\Module\core\Auth\Process;
  */
 class CardinalitySingleTest extends \PHPUnit\Framework\TestCase
 {
+    /** @var \SimpleSAML\Utils\HttpAdapter|\PHPUnit_Framework_MockObject_MockObject */
     private $http;
 
 
@@ -21,7 +22,11 @@ class CardinalitySingleTest extends \PHPUnit\Framework\TestCase
     {
         $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
         $_SERVER['REQUEST_METHOD'] = 'GET';
-        $filter = new \SimpleSAML\Module\core\Auth\Process\CardinalitySingle($config, null, $this->http);
+
+        /** @var \SimpleSAML\Utils\HttpAdapter $http */
+        $http = $this->http;
+
+        $filter = new \SimpleSAML\Module\core\Auth\Process\CardinalitySingle($config, null, $http);
         $filter->process($request);
         return $request;
     }
@@ -159,6 +164,7 @@ class CardinalitySingleTest extends \PHPUnit\Framework\TestCase
             ],
         ];
 
+        /** @psalm-suppress UndefinedMethod */
         $this->http->expects($this->once())
                    ->method('redirectTrustedURL');
 
diff --git a/tests/modules/core/lib/Auth/Process/CardinalityTest.php b/tests/modules/core/lib/Auth/Process/CardinalityTest.php
index 433944f6b95f98d928ca5a61aaf5fdd869fae1c5..3b666c25e31ce7deb784094eec2ab502128c01d2 100644
--- a/tests/modules/core/lib/Auth/Process/CardinalityTest.php
+++ b/tests/modules/core/lib/Auth/Process/CardinalityTest.php
@@ -7,6 +7,7 @@ namespace SimpleSAML\Test\Module\core\Auth\Process;
  */
 class CardinalityTest extends \PHPUnit\Framework\TestCase
 {
+    /** @var \SimpleSAML\Utils\HttpAdapter|\PHPUnit_Framework_MockObject_MockObject */
     private $http;
 
 
@@ -21,7 +22,11 @@ class CardinalityTest extends \PHPUnit\Framework\TestCase
     {
         $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
         $_SERVER['REQUEST_METHOD'] = 'GET';
-        $filter = new \SimpleSAML\Module\core\Auth\Process\Cardinality($config, null, $this->http);
+
+        /** @var \SimpleSAML\Utils\HttpAdapter $http */
+        $http = $this->http;
+
+        $filter = new \SimpleSAML\Module\core\Auth\Process\Cardinality($config, null, $http);
         $filter->process($request);
         return $request;
     }
@@ -117,6 +122,7 @@ class CardinalityTest extends \PHPUnit\Framework\TestCase
             ],
         ];
 
+        /** @psalm-suppress UndefinedMethod   It's a mock-object */
         $this->http->expects($this->once())
                    ->method('redirectTrustedURL');
 
@@ -139,6 +145,7 @@ class CardinalityTest extends \PHPUnit\Framework\TestCase
             ],
         ];
 
+        /** @psalm-suppress UndefinedMethod   It's a mock-object */
         $this->http->expects($this->once())
                    ->method('redirectTrustedURL');
 
@@ -159,6 +166,7 @@ class CardinalityTest extends \PHPUnit\Framework\TestCase
             'Attributes' => [],
         ];
 
+        /** @psalm-suppress UndefinedMethod   It's a mock-object */
         $this->http->expects($this->once())
                    ->method('redirectTrustedURL');
 
diff --git a/tests/modules/core/lib/Auth/UserPassBaseTest.php b/tests/modules/core/lib/Auth/UserPassBaseTest.php
index 3fc706060842232d0a0fe9a2ad3c38a60dc05150..7cecfbfc9a3c05497eb2914a4f7599bacd2c0538 100644
--- a/tests/modules/core/lib/Auth/UserPassBaseTest.php
+++ b/tests/modules/core/lib/Auth/UserPassBaseTest.php
@@ -17,11 +17,16 @@ class UserPassBaseTest extends \PHPUnit\Framework\TestCase
         $username = $_SERVER['PHP_AUTH_USER'] = 'username';
         $password = $_SERVER['PHP_AUTH_PW'] = 'password';
 
+        /** @var \SimpleSAML\Module\core\Auth\UserPassBase $stub */
         $stub = $this->getMockBuilder('\SimpleSAML\Module\core\Auth\UserPassBase')
             ->disableOriginalConstructor()
             ->setMethods(['login'])
             ->getMockForAbstractClass();
 
+        /** 
+         * @psalm-suppress InvalidArgument   Remove when PHPunit 8 is in place
+         * @psalm-suppress UndefinedMethod
+         */
         $stub->expects($this->once())
             ->method('login')
             ->with($username, $password)
@@ -38,7 +43,8 @@ class UserPassBaseTest extends \PHPUnit\Framework\TestCase
      */
     public function testAuthenticateECPMissingUsername()
     {
-        $this->expectException(\SimpleSAML\Error\Error::class, 'WRONGUSERPASS');
+        $this->expectException(\SimpleSAML\Error\Error::class);
+        $this->expectExceptionMessage('WRONGUSERPASS');
 
         $state = [
             'saml:Binding' => \SAML2\Constants::BINDING_PAOS,
@@ -47,6 +53,7 @@ class UserPassBaseTest extends \PHPUnit\Framework\TestCase
         unset($_SERVER['PHP_AUTH_USER']);
         $_SERVER['PHP_AUTH_PW'] = 'password';
 
+        /** @var \SimpleSAML\Module\core\Auth\UserPassBase $stub */
         $stub = $this->getMockBuilder('\SimpleSAML\Module\core\Auth\UserPassBase')
             ->disableOriginalConstructor()
             ->getMockForAbstractClass();
@@ -74,6 +81,7 @@ class UserPassBaseTest extends \PHPUnit\Framework\TestCase
             ->disableOriginalConstructor()
             ->getMockForAbstractClass();
 
+        /** @psalm-suppress UndefinedMethod   Remove when Psalm 3.x is in place */
         $stub->authenticate($state);
     }
 
@@ -93,18 +101,22 @@ class UserPassBaseTest extends \PHPUnit\Framework\TestCase
         $_SERVER['PHP_AUTH_USER'] = 'username';
         $password = $_SERVER['PHP_AUTH_PW'] = 'password';
 
+        /** @var \SimpleSAML\Module\core\Auth\UserPassBase $stub */
         $stub = $this->getMockBuilder('\SimpleSAML\Module\core\Auth\UserPassBase')
             ->disableOriginalConstructor()
             ->setMethods(['login'])
             ->getMockForAbstractClass();
 
+        /**
+         * @psalm-suppress InvalidArgument   Remove when PHPunit 8 is in place
+         * @psalm-suppress UndefinedMethod
+         */
         $stub->expects($this->once())
             ->method('login')
             ->with($forcedUsername, $password)
             ->will($this->returnValue($attributes));
 
         $stub->setForcedUsername($forcedUsername);
-
         $stub->authenticate($state);
     }
 }
diff --git a/tests/modules/core/lib/Auth/UserPassOrgBaseTest.php b/tests/modules/core/lib/Auth/UserPassOrgBaseTest.php
index 05a1a7ffd9a8c217cb60bb3fd8d0ade62c467561..cabf506c6e47b9ddeedb87f692c14e3e209f1fd6 100644
--- a/tests/modules/core/lib/Auth/UserPassOrgBaseTest.php
+++ b/tests/modules/core/lib/Auth/UserPassOrgBaseTest.php
@@ -28,6 +28,7 @@ class UserPassOrgBaseTest extends \PHPUnit\Framework\TestCase
             ]
         ];
 
+        /** @var \SimpleSAML\Module\core\Auth\UserPassOrgBase $mockUserPassOrgBase */
         $mockUserPassOrgBase = $this->getMockBuilder(\SimpleSAML\Module\core\Auth\UserPassOrgBase::class)
             ->setConstructorArgs([['AuthId' => 'my-org'], &$config])
             ->setMethods([])
diff --git a/tests/modules/core/lib/ControllerTest.php b/tests/modules/core/lib/ControllerTest.php
index b1125481f6aa742be45c9b80b7c2fcdf3179405c..4d3c5be2a79df7176b2d0f4a1778735a0dcd62f5 100644
--- a/tests/modules/core/lib/ControllerTest.php
+++ b/tests/modules/core/lib/ControllerTest.php
@@ -79,13 +79,18 @@ class ControllerTest extends ClearStateTestCase
     {
         $asConfig = Configuration::loadFromArray($this->authSources);
         Configuration::setPreLoadedConfig($asConfig, 'authsources.php');
+
         $request = new Request();
         $session = Session::getSessionFromRequest();
         $factory = new AuthenticationFactory($this->config, $session);
+
         $c = new Controller($this->config, $session, $factory);
+        /** @var \SimpleSAML\HTTP\RunnableResponse $response */
         $response = $c->login($request);
+
         $this->assertInstanceOf(RunnableResponse::class, $response);
         list($object, $method) = $response->getCallable();
+
         $this->assertInstanceOf(Simple::class, $object);
         $this->assertEquals('login', $method);
         $arguments = $response->getArguments();
@@ -112,12 +117,16 @@ class ControllerTest extends ClearStateTestCase
                 ]
             )
         );
+
         Configuration::setPreLoadedConfig($asConfig, 'authsources.php');
         $request = new Request();
         $session = Session::getSessionFromRequest();
         $factory = new AuthenticationFactory($this->config, $session);
+
         $c = new Controller($this->config, $session, $factory);
+        /** @var \SimpleSAML\XHTML\Template $response */
         $response = $c->login($request);
+
         $this->assertInstanceOf(Template::class, $response);
         $this->assertEquals('core:login.twig', $response->getTemplateName());
         $this->assertArrayHasKey('sources', $response->data);
@@ -152,10 +161,11 @@ class ControllerTest extends ClearStateTestCase
     {
         $asConfig = Configuration::loadFromArray($this->authSources);
         Configuration::setPreLoadedConfig($asConfig, 'authsources.php');
-        $request = new Request();
+
         $session = Session::getSessionFromRequest();
         $session->setConfiguration($this->config);
         $class = new \ReflectionClass($session);
+
         $authData = $class->getProperty('authData');
         $authData->setAccessible(true);
         $authData->setValue($session, [
@@ -167,8 +177,12 @@ class ControllerTest extends ClearStateTestCase
                 'Expire' => time() + 8 * 60* 60
             ]
         ]);
+
         $factory = new AuthenticationFactory($this->config, $session);
         $c = new Controller($this->config, $session, $factory);
+
+        $request = new Request();
+        /** @var \Symfony\Component\HttpFoundation\RedirectResponse $response */
         $response = $c->login($request);
         $this->assertInstanceOf(RedirectResponse::class, $response);
         $this->assertEquals(
diff --git a/tests/modules/core/lib/Storage/SQLPermanentStorageTest.php b/tests/modules/core/lib/Storage/SQLPermanentStorageTest.php
index 32964ade8c20951089f09d83690a64b1e8caaded..ee1217e405d5f45e8e6352663e39c7711df0f3a5 100644
--- a/tests/modules/core/lib/Storage/SQLPermanentStorageTest.php
+++ b/tests/modules/core/lib/Storage/SQLPermanentStorageTest.php
@@ -9,6 +9,7 @@ use PHPUnit\Framework\TestCase;
  */
 class SQLPermanentStorageTest extends TestCase
 {
+    /** @var \SimpleSAML\Module\core\Storage\SQLPermanentStorage */
     private static $sql;
 
 
@@ -44,6 +45,7 @@ class SQLPermanentStorageTest extends TestCase
         self::$sql->set('testtype', 'testkey1', 'testkey2', 'testvalue', 2);
 
         // Test getCondition
+        /** @var array $result */
         $result = self::$sql->get();
         $this->assertEquals('testvalue', $result['value']);
     }
@@ -61,6 +63,7 @@ class SQLPermanentStorageTest extends TestCase
         $result = self::$sql->getValue('testtype', 'testkey1', 'testkey2');
         $this->assertEquals('testvaluemodified', $result);
 
+        /** @var array $result */
         $result = self::$sql->getList('testtype', 'testkey1', 'testkey2');
         $this->assertEquals('testvaluemodified', $result[0]['value']);
     }
diff --git a/tests/modules/multiauth/lib/Auth/Source/MultiAuthTest.php b/tests/modules/multiauth/lib/Auth/Source/MultiAuthTest.php
index e9eeabd42085782a89da2dd1e00efd02f64eb745..0d6633faacd08c941df8326e1ef3da290ae710ce 100644
--- a/tests/modules/multiauth/lib/Auth/Source/MultiAuthTest.php
+++ b/tests/modules/multiauth/lib/Auth/Source/MultiAuthTest.php
@@ -8,6 +8,9 @@ use SimpleSAML\Module\multiauth\Auth\Source\MultiAuth;
 
 class MultiAuthTest extends \SimpleSAML\Test\Utils\ClearStateTestCase
 {
+    /** @var Configuration */
+    private $config;
+
     /** @var Configuration */
     private $sourceConfig;
 
diff --git a/tests/modules/saml/lib/Auth/Source/Auth_Source_SP_Test.php b/tests/modules/saml/lib/Auth/Source/Auth_Source_SP_Test.php
index 29549d28370fbc4c73e75cd5fa93ab92e6a71674..147e5e35b7526975727ac3a421fa1563ed161b77 100644
--- a/tests/modules/saml/lib/Auth/Source/Auth_Source_SP_Test.php
+++ b/tests/modules/saml/lib/Auth/Source/Auth_Source_SP_Test.php
@@ -16,6 +16,7 @@ use SimpleSAML\Test\Utils\ClearStateTestCase;
  */
 class ExitTestException extends \Exception
 {
+    /** @var array */
     private $testResult;
 
 
@@ -93,10 +94,15 @@ class SPTester extends \SimpleSAML\Module\saml\Auth\Source\SP
  */
 class SPTest extends ClearStateTestCase
 {
+    /** @var \SimpleSAML\Configuration|null $idpMetadata */
     private $idpMetadata = null;
 
+    /** @var array $idpConfigArray */
     private $idpConfigArray;
 
+    /** @var \SimpleSAML\Configuration */
+    private $config;
+
 
     /**
      * @return \SimpleSAML\Configuration
@@ -198,11 +204,14 @@ class SPTest extends ClearStateTestCase
         // Assert values in the generated AuthnRequest
         /** @var \DOMElement $xml */
         $xml = $ar->toSignedXML();
+
+        /** @var \DOMAttr[] $q */
         $q = \SAML2\Utils::xpQuery($xml, '/samlp:AuthnRequest/@Destination');
         $this->assertEquals(
             $this->idpConfigArray['SingleSignOnService'][0]['Location'],
             $q[0]->value
         );
+
         $q = \SAML2\Utils::xpQuery($xml, '/samlp:AuthnRequest/saml:Issuer');
         $this->assertEquals(
             'http://localhost/simplesaml/module.php/saml/sp/metadata.php/default-sp',
@@ -225,17 +234,21 @@ class SPTest extends ClearStateTestCase
         /** @var \SAML2\AuthnRequest $ar */
         $ar = $this->createAuthnRequest($state);
 
+        /** @var \SAML2\XML\saml\NameID $nameID */
         $nameID = $ar->getNameId();
         $this->assertEquals($state['saml:NameID']['Value'], $nameID->getValue());
         $this->assertEquals($state['saml:NameID']['Format'], $nameID->getFormat());
 
         /** @var \DOMElement $xml */
         $xml = $ar->toSignedXML();
+
+        /** @var \DOMAttr[] $q */
         $q = \SAML2\Utils::xpQuery($xml, '/samlp:AuthnRequest/saml:Subject/saml:NameID/@Format');
         $this->assertEquals(
             $state['saml:NameID']['Format'],
             $q[0]->value
         );
+
         $q = \SAML2\Utils::xpQuery($xml, '/samlp:AuthnRequest/saml:Subject/saml:NameID');
         $this->assertEquals(
             $state['saml:NameID']['Value'],
@@ -258,6 +271,7 @@ class SPTest extends ClearStateTestCase
         /** @var \SAML2\AuthnRequest $ar */
         $ar = $this->createAuthnRequest($state);
 
+        /** @var array $a */
         $a = $ar->getRequestedAuthnContext();
         $this->assertEquals(
             $state['saml:AuthnContextClassRef'],
@@ -266,6 +280,7 @@ class SPTest extends ClearStateTestCase
 
         /** @var \DOMElement $xml */
         $xml = $ar->toSignedXML();
+
         $q = \SAML2\Utils::xpQuery($xml, '/samlp:AuthnRequest/samlp:RequestedAuthnContext/saml:AuthnContextClassRef');
         $this->assertEquals(
             $state['saml:AuthnContextClassRef'],
@@ -281,6 +296,7 @@ class SPTest extends ClearStateTestCase
      */
     public function testForcedAuthn()
     {
+        /** @var bool $state['ForceAuthn'] */
         $state = [
             'ForceAuthn' => true
         ];
@@ -295,6 +311,8 @@ class SPTest extends ClearStateTestCase
 
         /** @var \DOMElement $xml */
         $xml = $ar->toSignedXML();
+
+        /** @var \DOMAttr[] $q */
         $q = \SAML2\Utils::xpQuery($xml, '/samlp:AuthnRequest/@ForceAuthn');
         $this->assertEquals(
             $state['ForceAuthn'] ? 'true' : 'false',
@@ -380,6 +398,8 @@ class SPTest extends ClearStateTestCase
             /** @var AuthnRequest $ar */
             $ar = $r['ar'];
             $xml = $ar->toSignedXML();
+
+            /** @var \DOMAttr[] $q */
             $q = \SAML2\Utils::xpQuery($xml, '/samlp:AuthnRequest/@Destination');
             $this->assertEquals(
                 'https://saml.idp/sso/',
@@ -418,6 +438,8 @@ class SPTest extends ClearStateTestCase
             /** @var AuthnRequest $ar */
             $ar = $r['ar'];
             $xml = $ar->toSignedXML();
+
+            /** @var \DOMAttr[] $q */
             $q = \SAML2\Utils::xpQuery($xml, '/samlp:AuthnRequest/@Destination');
             $this->assertEquals(
                 'https://saml.idp/sso/',
diff --git a/tests/modules/saml/lib/IdP/SAML2Test.php b/tests/modules/saml/lib/IdP/SAML2Test.php
index ebce96d27d7255203d4ec82519e5b6871d8f12f3..cdd5d6df4ba44737d0e15907542e918e58fec8a5 100644
--- a/tests/modules/saml/lib/IdP/SAML2Test.php
+++ b/tests/modules/saml/lib/IdP/SAML2Test.php
@@ -156,11 +156,11 @@ class SAML2Test extends ClearStateTestCase
      * Callers should validate the return state array or confirm appropriate exceptions are returned.
      *
      * @param array $queryParams
-     * @return string[] The state array used for handling the authentication request.
+     * @return array The state array used for handling the authentication request.
      */
     private function idpInitiatedHelper(array $queryParams)
     {
-        /** @var \PHPUnit_Framework_MockObject_MockObject|\SimpleSAML\IdP $idpStub */
+        /** @var \PHPUnit_Framework_MockObject_MockObject $idpStub */
         $idpStub = $this->getMockBuilder(IdP::class)
             ->disableOriginalConstructor()
             ->getMock();
@@ -169,6 +169,7 @@ class SAML2Test extends ClearStateTestCase
             'saml20.ecp' => true, //enable additional bindings so we can test selection logic
         ]);
 
+        /** @psalm-suppress UndefinedMethod   Remove when Psalm 3.x is in place */
         $idpStub->method("getConfig")
             ->willReturn($idpMetadata);
 
@@ -198,13 +199,22 @@ EOT;
 
 
         $state = [];
+
+        /** @psalm-suppress InvalidArgument   Remove when PHPunit 8 is in place */
         $idpStub->expects($this->once())
             ->method('handleAuthenticationRequest')
-            ->with($this->callback(function ($arg) use (&$state) {
-                $state = $arg;
-                return true;
-            }));
-
+            ->with($this->callback(
+                /**
+                 * @param array $arg
+                 * @return bool
+                 */
+                function ($arg) use (&$state) {
+                    $state = $arg;
+                    return true;
+                }
+            ));
+
+        /** @psalm-suppress InvalidArgument */
         SAML2::receiveAuthnRequest($idpStub);
 
         return $state;
diff --git a/tests/www/IndexTest.php b/tests/www/IndexTest.php
index 7d082b7015b3f11b6c75e2422017fde025713462..d2bec0f51dd0ecfb6544583a2c9d64350f8ecf5d 100644
--- a/tests/www/IndexTest.php
+++ b/tests/www/IndexTest.php
@@ -17,7 +17,6 @@ use \SimpleSAML\Test\BuiltInServer;
 
 class IndexTest extends TestCase
 {
-
     /**
      * @var \SimpleSAML\Test\BuiltInServer
      */