diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index bd3302fbcc948107560f714a635c0513bc310c83..d06104fa8d9eb8c371db73cb48845b9e04d4d6ed 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -43,9 +43,19 @@ lint:php-mass-detection:
     WITH_XDEBUG: "true"
   before_script:
     - apt-get update; apt-get install -y unzip
-    - composer install --optimize-autoloader --no-interaction --no-ansi --prefer-dist
+    - php -v
+    - php -r 'echo sys_get_temp_dir() . PHP_EOL;'
+    - composer --no-ansi self-update
+    - composer --no-ansi --version
+    - composer install --optimize-autoloader --classmap-authoritative --no-interaction --prefer-dist --no-ansi
   script:
     - ./vendor/bin/phpunit --coverage-text --colors=never
+  after_script:
+    - mv -f `php -r 'echo sys_get_temp_dir();'`/composer-test .build
+  artifacts:
+    when: on_failure
+    paths:
+      - .build/
 
 test:5.4:
   <<: *test
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 4452416103046aeeed8a99ced738eaad13cbe53e..37fc7dcd5a4945b3f2562b5976daf76c47c77502 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -10,6 +10,10 @@
          syntaxCheck="false"
          bootstrap="tests/bootstrap.php">
 
+    <php>
+        <ini name="memory_limit" value="-1"/>
+    </php>
+
     <testsuites>
         <testsuite name="Unit Tests">
             <directory>./tests/Unit/</directory>
@@ -17,6 +21,9 @@
         <testsuite name="Integration Tests">
             <directory>./tests/Integration/</directory>
         </testsuite>
+        <testsuite name="Functional Tests">
+            <directory>./tests/Functional/</directory>
+        </testsuite>
     </testsuites>
 
     <filter>
diff --git a/tests/Fixtures/Composer/simple-standard/composer.json b/tests/Fixtures/Composer/simple-standard/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..17fc11a3b0d5761ea08f42cf2ac80f617f75077d
--- /dev/null
+++ b/tests/Fixtures/Composer/simple-standard/composer.json
@@ -0,0 +1,9 @@
+{
+  "name": "higidi/testcase-simple-standard",
+  "description": "Testcase with simple standard.",
+  "type": "phpcodesniffer-standard",
+  "require": {
+    "higidi/composer-phpcodesniffer-standards-plugin": "dev-workingDir"
+  },
+  "version": "1.0.0"
+}
diff --git a/tests/Fixtures/Composer/simple-standard/ruleset.xml b/tests/Fixtures/Composer/simple-standard/ruleset.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c0ecd7732cc82fb68e57b11a18eb3f76c06a0c1c
--- /dev/null
+++ b/tests/Fixtures/Composer/simple-standard/ruleset.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<ruleset name="Simple-Standard">
+    <description>Testcase with simple standard.</description>
+</ruleset>
diff --git a/tests/Functional/ComposerTestCase.php b/tests/Functional/ComposerTestCase.php
new file mode 100644
index 0000000000000000000000000000000000000000..392a1546bff122c0023cfee1043ee881fe3c07b6
--- /dev/null
+++ b/tests/Functional/ComposerTestCase.php
@@ -0,0 +1,202 @@
+<?php
+
+namespace Higidi\ComposerPhpCSStandardsPlugin\Tests\Functional;
+
+use Composer\Console\Application;
+use Composer\Factory;
+use Composer\Json\JsonFile;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Tester\ApplicationTester;
+use Symfony\Component\Filesystem\Filesystem;
+
+/**
+ * Base functional test case.
+ */
+class ComposerTestCase extends TestCase
+{
+    /**
+     * @var string
+     */
+    protected $oldWorkingDirectory;
+
+    /**
+     * @var string
+     */
+    protected $testWorkingDir;
+
+    /**
+     * @var Application
+     */
+    protected $application;
+
+    /**
+     * @var ApplicationTester
+     */
+    protected $applicationTester;
+
+    /**
+     * @return void
+     */
+    protected function setUp()
+    {
+        parent::setUp();
+        $this->oldWorkingDirectory = getcwd();
+        $this->testWorkingDir = $this->createUniqueTmpDirectory();
+        chdir($this->testWorkingDir);
+        $this->application = new Application();
+        $this->application->setAutoExit(false);
+        $this->applicationTester = new ApplicationTester($this->application);
+    }
+
+    /**
+     * @return void
+     */
+    protected function tearDown()
+    {
+        parent::tearDown();
+        chdir($this->oldWorkingDirectory);
+        if ($this->testWorkingDir && $this->hasPassed()) {
+            $fs = new Filesystem();
+            $fs->remove($this->testWorkingDir);
+        }
+        unset($this->oldWorkingDirectory, $this->testWorkingDir);
+    }
+
+    /**
+     * @return bool
+     */
+    protected function hasPassed()
+    {
+        return $this->getStatus() === \PHPUnit_Runner_BaseTestRunner::STATUS_PASSED;
+    }
+
+    /**
+     * @return string
+     */
+    protected function createUniqueTmpDirectory()
+    {
+        $attempts = 5;
+        $randomize = false;
+        $root = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'composer-test';
+        $name = $this->getName();
+        $folderName = preg_replace('/[^a-zA-Z0-9\-\._]/', '', $name);
+
+        do {
+            $unique = $root . DIRECTORY_SEPARATOR . $folderName;
+            if ($randomize) {
+                $unique .= '-' . uniqid(rand(1000, 9000));
+            }
+
+            $fs = new Filesystem();
+            if (! file_exists($unique)) {
+                $fs->mkdir($unique);
+
+                return realpath($unique);
+            }
+            $randomize = true;
+        } while (--$attempts);
+
+        throw new \RuntimeException('Failed to create a unique temporary directory.');
+    }
+
+    /**
+     * @return string
+     */
+    protected function getComposerJsonFilePath()
+    {
+        return Factory::getComposerFile();
+    }
+
+    /**
+     * @return JsonFile
+     */
+    protected function getComposerJsonFile()
+    {
+        $jsonFile = new JsonFile($this->getComposerJsonFilePath());
+
+        return $jsonFile;
+    }
+
+    /**
+     * @return bool|string
+     */
+    protected function getLocalPackagePath()
+    {
+        return realpath(__DIR__ . '/../../');
+    }
+
+    /**
+     * @return string
+     */
+    protected function getLocalPackageComposerPath()
+    {
+        return $this->getLocalPackagePath() . DIRECTORY_SEPARATOR . 'composer.json';
+    }
+
+    /**
+     * @return mixed
+     */
+    protected function getLocalPackageComposerPackage()
+    {
+        $jsonFile = new JsonFile($this->getLocalPackageComposerPath());
+
+        $json = $jsonFile->read();
+        if (! is_array($json)) {
+            throw new \RuntimeException();
+        }
+        $json['version'] = 'dev-workingDir';
+        $json['dist'] = [
+            'type' => 'path',
+            'url' => $this->getLocalPackagePath(),
+        ];
+
+        return $json;
+    }
+
+    /**
+     * @param array $requireDev
+     * @param array $require
+     * @param array $additionalJson
+     *
+     * @return void
+     */
+    protected function writeComposerJson(array $requireDev, array $require = [], array $additionalJson = [])
+    {
+        $defaultJson = [
+            'repositories' => [
+                [
+                    'type' => 'package',
+                    'package' => $this->getLocalPackageComposerPackage(),
+                ],
+                [
+                    'type' => 'path',
+                    'url' => implode(
+                        DIRECTORY_SEPARATOR,
+                        [$this->getLocalPackagePath(), 'tests', 'Fixtures', 'Composer', '*']
+                    ),
+                ],
+            ],
+            'minimum-stability' => 'dev',
+            'config' => [
+                'bin-dir' => 'bin',
+                'vendor-dir' => 'vendor',
+            ],
+            'require-dev' => [
+                'squizlabs/php_codesniffer' => '*',
+            ],
+        ];
+        $json = [
+            'require-dev' => $requireDev,
+        ];
+        if (count($require) > 0) {
+            $json['require'] = $require;
+        }
+        $json = array_replace_recursive($defaultJson, $json);
+        if (count($additionalJson) > 0) {
+            $json = array_replace_recursive($json, $additionalJson);
+        }
+        $jsonFile = $this->getComposerJsonFile();
+
+        $jsonFile->write($json);
+    }
+}
diff --git a/tests/Functional/InstallTest.php b/tests/Functional/InstallTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..5ca601bfb0c6178f2f1372b481d58231e66474d7
--- /dev/null
+++ b/tests/Functional/InstallTest.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Higidi\ComposerPhpCSStandardsPlugin\Tests\Functional;
+
+class InstallTest extends ComposerTestCase
+{
+    /**
+     * @test
+     */
+    public function itInstallPhpCodeSnifferStandards()
+    {
+        $this->writeComposerJson(
+            [
+                'higidi/testcase-simple-standard' => '1.0.0',
+            ]
+        );
+        $srcPath = implode(
+            DIRECTORY_SEPARATOR,
+            [$this->getLocalPackagePath(), 'tests', 'fixtures', 'Composer', 'simple-standard', 'ruleset.xml']
+        );
+        $dstPath = implode(
+            DIRECTORY_SEPARATOR,
+            [
+                $this->testWorkingDir,
+                'vendor',
+                'squizlabs',
+                'php_codesniffer',
+                'CodeSniffer',
+                'Standards',
+                'Simple-Standard',
+                'ruleset.xml',
+            ]
+        );
+
+        $exitCode = $this->applicationTester->run(['install']);
+
+        $this->assertSame(0, $exitCode);
+        $this->assertFileExists($dstPath);
+        $this->assertFileEquals($srcPath, $dstPath);
+    }
+}
diff --git a/tests/Functional/UninstallTest.php b/tests/Functional/UninstallTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..68f8d43dfc206627906203a999ada47a6ad9f3ee
--- /dev/null
+++ b/tests/Functional/UninstallTest.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Higidi\ComposerPhpCSStandardsPlugin\Tests\Functional;
+
+class UninstallTest extends ComposerTestCase
+{
+    /**
+     * @test
+     */
+    public function itUninstallPhpCodeSnifferStandards()
+    {
+        $this->writeComposerJson(
+            [
+                'higidi/testcase-simple-standard' => '1.0.0',
+            ]
+        );
+        $srcPath = implode(
+            DIRECTORY_SEPARATOR,
+            [$this->getLocalPackagePath(), 'tests', 'fixtures', 'Composer', 'simple-standard', 'ruleset.xml']
+        );
+        $dstPath = implode(
+            DIRECTORY_SEPARATOR,
+            [
+                $this->testWorkingDir,
+                'vendor',
+                'squizlabs',
+                'php_codesniffer',
+                'CodeSniffer',
+                'Standards',
+                'Simple-Standard',
+                'ruleset.xml',
+            ]
+        );
+
+        $exitCode = $this->applicationTester->run(['install']);
+
+        $this->assertSame(0, $exitCode);
+        $this->assertFileExists($dstPath);
+        $this->assertFileEquals($srcPath, $dstPath);
+
+        $exitCode = $this->applicationTester->run(
+            ['command' => 'remove', 'packages' => ['higidi/testcase-simple-standard']]
+        );
+
+        $this->assertSame(0, $exitCode);
+        $this->assertFileNotExists($dstPath);
+    }
+}