diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000000000000000000000000000000000000..5e06b16ad17033fae84e3b3f7632f9733f2208ec
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
+    backupGlobals="false"
+    colors="true"
+    bootstrap="vendor/autoload.php"
+    verbose="true"
+>
+    <testsuites>
+        <testsuite name="docker4drupal">
+            <directory>./tests/</directory>
+        </testsuite>
+    </testsuites>
+</phpunit>
diff --git a/tests/PluginTest.php b/tests/PluginTest.php
index bae2fdea8c764ae09ea9f671fe46870cf32dd404..ad1e49299ae99d403776b03b31b1e6254f4ddc30 100644
--- a/tests/PluginTest.php
+++ b/tests/PluginTest.php
@@ -7,6 +7,8 @@
 
 namespace LakeDrops\Docker4Drupal\Tests;
 
+use Composer\Util\Filesystem;
+
 /**
  * Tests composer plugin functionality.
  */
@@ -14,11 +16,156 @@ class PluginTest extends \PHPUnit_Framework_TestCase {
 
   static $name = 'docker4drupal';
 
+  /**
+   * @var \Composer\Util\Filesystem
+   */
+  protected $fs;
+
+  /**
+   * @var string
+   */
+  protected $tmpDir;
+
+  /**
+   * @var string
+   */
+  protected $rootDir;
+
+  /**
+   * @var string
+   */
+  protected $tmpReleaseTag;
+
   /**
    * SetUp test
    */
   public function setUp() {
     $this->rootDir = realpath(realpath(__DIR__ . '/..'));
+
+    // Prepare temp directory.
+    $this->fs = new Filesystem();
+    $this->tmpDir = realpath(sys_get_temp_dir()) . DIRECTORY_SEPARATOR . self::$name;
+    $this->ensureDirectoryExistsAndClear($this->tmpDir);
+
+    $this->writeTestReleaseTag();
+    $this->writeComposerJSON();
+
+    chdir($this->tmpDir);
+  }
+
+  /**
+   * tearDown
+   *
+   * @return void
+   */
+  public function tearDown() {
+    $this->fs->removeDirectory($this->tmpDir);
+    $this->git(sprintf('tag -d "%s"', $this->tmpReleaseTag));
+  }
+
+  /**
+   * Tests a simple composer install without core, but adding core later.
+   */
+  public function testComposerInstallAndUpdate() {
+    # TODO: Write the actual tests
+  }
+
+  /**
+   * Writes the default composer json to the temp direcoty.
+   */
+  protected function writeComposerJSON() {
+    $json = json_encode($this->composerJSONDefaults(), JSON_PRETTY_PRINT);
+    file_put_contents($this->tmpDir . '/composer.json', $json);
+  }
+
+  /**
+   * Writes a tag for the current commit, so we can reference it directly in the
+   * composer.json.
+   */
+  protected function writeTestReleaseTag() {
+    $this->tmpReleaseTag = '999.0.' . time();
+    $this->git(sprintf('tag -a "%s" -m "%s"', $this->tmpReleaseTag, 'Tag for testing this exact commit'));
+  }
+
+  /**
+   * Provides the default composer.json data.
+   *
+   * @return array
+   */
+  protected function composerJSONDefaults() {
+    return array(
+      'repositories' => array(
+        array(
+          'type' => 'vcs',
+          'url' => $this->rootDir,
+        ),
+        array(
+          'type' => 'composer',
+          'url' => 'https://packages.drupal.org/8',
+        ),
+      ),
+      'require' => array(
+        'lakedrops/' . self::$name => $this->tmpReleaseTag,
+        'composer/installers' => '~1.2.0',
+        'drupal/core' => '~8.2.0',
+        'drupal-composer/drupal-scaffold' => '~2.2.0',
+      ),
+      'scripts' => array(
+        self::$name => 'LakeDrops\\Drupal8Scaffold\\Plugin::configProject',
+      ),
+      'minimum-stability' => 'dev',
+      'extra' => array(
+        'installer-paths' => array(
+          'web/core' => array('type:drupal-core'),
+          'web/modules/contrib/{$name}' => array('type:drupal-module'),
+          'web/profiles/contrib/{$name}' => array('type:drupal-profile'),
+          'web/themes/contrib/{$name}' => array('type:drupal-theme'),
+          'drush/contrib/{$name}' => array('type:drupal-drush'),
+        ),
+      )
+    );
+  }
+
+  /**
+   * Wrapper for the composer command.
+   *
+   * @param string $command
+   *   Composer command name, arguments and/or options
+   * @throws \Exception
+   */
+  protected function composer($command) {
+    chdir($this->tmpDir);
+    passthru(escapeshellcmd($this->rootDir . '/vendor/bin/composer ' . $command), $exit_code);
+    if ($exit_code !== 0) {
+      throw new \Exception('Composer returned a non-zero exit code');
+    }
+  }
+
+  /**
+   * Wrapper for git command in the root directory.
+   *
+   * @param $command
+   *   Git command name, arguments and/or options.
+   * @throws \Exception
+   */
+  protected function git($command) {
+    chdir($this->rootDir);
+    passthru(escapeshellcmd('git -c "user.email=phpunit@test.com" ' . $command), $exit_code);
+    if ($exit_code !== 0) {
+      throw new \Exception('Git returned a non-zero exit code');
+    }
+  }
+
+  /**
+   * Makes sure the given directory exists and has no content.
+   *
+   * @param string $directory
+   */
+  protected function ensureDirectoryExistsAndClear($directory) {
+    if (is_dir($directory)) {
+      $this->fs->removeDirectory($directory);
+    }
+    mkdir($directory, 0777, TRUE);
   }
 
 }