diff --git a/core/modules/migrate/src/Plugin/migrate/source/DummyQueryTrait.php b/core/modules/migrate/src/Plugin/migrate/source/DummyQueryTrait.php
new file mode 100644
index 0000000..42a9943
--- /dev/null
+++ b/core/modules/migrate/src/Plugin/migrate/source/DummyQueryTrait.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\migrate\Plugin\migrate\source\DummyQueryTrait.
+ */
+
+namespace Drupal\migrate\Plugin\migrate\source;
+
+/**
+ * Trait providing a dummy select query object for source plugins based on
+ * SqlBase which override initializeIterator() to obtain their data from other
+ * SqlBase services instead of a direct query. This ensures that query() returns
+ * a valid object, even though it isn't used for iteration.
+ */
+trait DummyQueryTrait {
+
+  /**
+   * @return \Drupal\Core\Database\Query\SelectInterface
+   */
+  public function query() {
+    // Pass an arbritrary table name - the query should never be executed anyway.
+    $query = $this->select(uniqid(), 's')
+      ->range(0, 1);
+    $query->addExpression('1');
+    return $query;
+  }
+
+}
diff --git a/core/modules/migrate/src/Tests/MigrateTestBase.php b/core/modules/migrate/src/Tests/MigrateTestBase.php
index c3fd6ce..8cb1f5c 100644
--- a/core/modules/migrate/src/Tests/MigrateTestBase.php
+++ b/core/modules/migrate/src/Tests/MigrateTestBase.php
@@ -45,6 +45,13 @@
    */
   protected $migrateMessages;
 
+  /**
+   * The primary migration being tested.
+   *
+   * @var MigrationInterface
+   */
+  protected $migration;
+
   public static $modules = array('migrate');
 
   /**
@@ -132,12 +139,15 @@ protected function prepareMigrations(array $id_mappings) {
    */
   protected function executeMigration($migration) {
     if (is_string($migration)) {
-      $migration = Migration::load($migration);
+      $this->migration = Migration::load($migration);
+    }
+    else {
+      $this->migration = $migration;
     }
     if ($this instanceof MigrateDumpAlterInterface) {
       static::migrateDumpAlter($this);
     }
-    (new MigrateExecutable($migration, $this))->import();
+    (new MigrateExecutable($this->migration, $this))->import();
   }
 
   /**
diff --git a/core/modules/migrate/tests/src/Unit/MigrateSqlSourceTestCase.php b/core/modules/migrate/tests/src/Unit/MigrateSqlSourceTestCase.php
index 98180d5..e644a04 100644
--- a/core/modules/migrate/tests/src/Unit/MigrateSqlSourceTestCase.php
+++ b/core/modules/migrate/tests/src/Unit/MigrateSqlSourceTestCase.php
@@ -59,6 +59,13 @@
   protected $expectedResults = array();
 
   /**
+   * Expected count of source rows.
+   *
+   * @var int
+   */
+  protected $expectedCount = 0;
+
+  /**
    * The source plugin instance under test.
    *
    * @var \Drupal\migrate\Plugin\MigrateSourceInterface
@@ -97,6 +104,7 @@ protected function setUp() {
       ->method('getSourcePlugin')
       ->will($this->returnValue($plugin));
     $this->source = $plugin;
+    $this->expectedCount = count($this->expectedResults);
   }
 
   /**
@@ -107,6 +115,13 @@ public function testRetrieval() {
   }
 
   /**
+   * Test the source returns the row count expected.
+   */
+  public function testSourceCount() {
+    $this->assertEquals($this->source->count(), $this->expectedCount);
+  }
+
+  /**
    * @param \Drupal\migrate\Row $row
    * @param string $key
    * @return mixed
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/CommentVariable.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/CommentVariable.php
index 8dfb5a8..e01d28d 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/CommentVariable.php
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/CommentVariable.php
@@ -8,6 +8,7 @@
 namespace Drupal\migrate_drupal\Plugin\migrate\source\d6;
 
 use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
+use Drupal\migrate\Plugin\migrate\source\DummyQueryTrait;
 
 /**
  * @MigrateSource(
@@ -16,6 +17,8 @@
  */
 class CommentVariable extends DrupalSqlBase {
 
+  use DummyQueryTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -95,13 +98,6 @@ protected function commentPrefixes() {
   /**
    * {@inheritdoc}
    */
-  public function query() {
-    // Nothing to do here.
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function getIds() {
     $ids['node_type']['type'] = 'string';
     return $ids;
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UploadInstance.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UploadInstance.php
index 6d5f891..3b24f5b 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UploadInstance.php
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UploadInstance.php
@@ -8,6 +8,7 @@
 namespace Drupal\migrate_drupal\Plugin\migrate\source\d6;
 
 use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
+use Drupal\migrate\Plugin\migrate\source\DummyQueryTrait;
 
 /**
  * Drupal 6 upload instance source from database.
@@ -19,6 +20,8 @@
  */
 class UploadInstance extends DrupalSqlBase {
 
+  use DummyQueryTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -63,13 +66,6 @@ public function getIds() {
   /**
    * {@inheritdoc}
    */
-  public function query() {
-    // Nothing needed here.
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function fields() {
     return array(
       'node_type' => $this->t('Node type'),
@@ -78,4 +74,11 @@ public function fields() {
     );
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function count() {
+    return count($this->initializeIterator());
+  }
+
 }
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UserPictureInstance.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UserPictureInstance.php
index 667a551..f3a9654 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UserPictureInstance.php
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UserPictureInstance.php
@@ -8,6 +8,7 @@
 namespace Drupal\migrate_drupal\Plugin\migrate\source\d6;
 
 use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
+use Drupal\migrate\Plugin\migrate\source\DummyQueryTrait;
 
 /**
  * Drupal 6 user picture field instance source.
@@ -20,6 +21,8 @@
  */
 class UserPictureInstance extends DrupalSqlBase {
 
+  use DummyQueryTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -36,6 +39,15 @@ public function initializeIterator() {
   /**
    * {@inheritdoc}
    */
+  public function count() {
+    // This source provides a single row, corresponding to a single picture
+    // field to be added to the user entity.
+    return 1;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function fields() {
     return array(
       'file_directory' => 'The directory to store images..',
@@ -52,11 +64,4 @@ public function getIds() {
     return $ids;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function query() {
-    // Nothing to do here.
-  }
-
 }
diff --git a/core/modules/migrate_drupal/src/Tests/MigrateDrupalTestBase.php b/core/modules/migrate_drupal/src/Tests/MigrateDrupalTestBase.php
index d6bf706..9d4e1a2 100644
--- a/core/modules/migrate_drupal/src/Tests/MigrateDrupalTestBase.php
+++ b/core/modules/migrate_drupal/src/Tests/MigrateDrupalTestBase.php
@@ -10,6 +10,8 @@
 use Drupal\migrate\Tests\MigrateTestBase;
 use Drupal\migrate\Entity\Migration;
 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
+use Drupal\migrate\Plugin\migrate\source\SqlBase;
+use Drupal\Core\Database\Query\SelectInterface;
 
 /**
  * Base class for Drupal migration tests.
@@ -74,4 +76,21 @@ protected function installMigrations($version) {
       }
     }
   }
+
+  /**
+   * Test that the source plugin provides a valid query and a valid count.
+   */
+  public function testSourcePlugin() {
+    if (isset($this->migration)) {
+      $source = $this->migration->getSourcePlugin();
+      // Make sure a SqlBase source has a valid query.
+      if ($source instanceof SqlBase) {
+        /** @var SqlBase $source */
+        $this->assertTrue($source->query() instanceof SelectInterface, 'SQL source plugin has valid query');
+      }
+      // Validate that any source returns a valid count.
+      $this->assertTrue(is_numeric($source->count()), 'Source plugin returns valid count');
+    }
+  }
+
 }
diff --git a/core/modules/migrate_drupal/tests/src/Unit/source/d6/TermSourceWithVocabularyFilterTest.php b/core/modules/migrate_drupal/tests/src/Unit/source/d6/TermSourceWithVocabularyFilterTest.php
index f209b0d..5b79e2f 100644
--- a/core/modules/migrate_drupal/tests/src/Unit/source/d6/TermSourceWithVocabularyFilterTest.php
+++ b/core/modules/migrate_drupal/tests/src/Unit/source/d6/TermSourceWithVocabularyFilterTest.php
@@ -23,5 +23,8 @@ protected function setUp() {
     $this->expectedResults = array_values(array_filter($this->expectedResults, function($result) {
       return $result['vid'] == 5;
     }));
+    // We know there are two rows with vid == 5.
+    $this->expectedCount = 2;
   }
+
 }
