diff --git a/core/modules/system/tests/src/Unit/Entity/EntityPropertiesVisibilityTest.php b/core/modules/system/tests/src/Unit/Entity/EntityPropertiesVisibilityTest.php
new file mode 100644
index 0000000..7050362
--- /dev/null
+++ b/core/modules/system/tests/src/Unit/Entity/EntityPropertiesVisibilityTest.php
@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\system\Unit\Entity\EntityPropertiesVisibilityTest.
+ */
+
+namespace Drupal\Tests\system\Unit\Entity;
+
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests properties visibility.
+ *
+ * Tests that all class variables of subclasses of ContentEntityBase and
+ * ConfigEntityBase are protected or are allowed to be public.
+ *
+ * @group Entity
+ */
+class EntityPropertiesVisibilityTest extends UnitTestCase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // The variable Vocabulary::hierarchy has a default value of
+    // TAXONOMY_HIERARCHY_DISABLED. The problem is that this constant is defined
+    // in the file taxonomy.module.
+    require_once $this->root . '/core/modules/taxonomy/taxonomy.module';
+
+    // The constant DRUPAL_OPTIONAL has to be included.
+    require_once $this->root . '/core/modules/system/system.module';
+  }
+
+  /**
+   * Data provider for testEntityVariablesAreProtectedOrAreAllowedPublic.
+   *
+   * @return array
+   *   Array of arrays with the following elements:
+   *   - An array with the expected public class variables.
+   *   - String with the to be tested class.
+   */
+  public function providerEntityVariablesAreProtectedOrAreAllowedPublic() {
+    return [
+      // Subclasses of ContentEntityBase.
+      [[], 'Drupal\block_content\Entity\BlockContent'],
+      [[], 'Drupal\comment\Entity\Comment'],
+      [[], 'Drupal\aggregator\Entity\Feed'],
+      [[], 'Drupal\file\Entity\File'],
+      [[], 'Drupal\aggregator\Entity\Item'],
+      [[], 'Drupal\menu_link_content\Entity\MenuLinkContent'],
+      [[], 'Drupal\contact\Entity\Message'],
+      [['in_preview'], 'Drupal\node\Entity\Node'],
+      [[], 'Drupal\shortcut\Entity\Shortcut'],
+      [[], 'Drupal\taxonomy\Entity\Term'],
+      [[], 'Drupal\user\Entity\User'],
+      // Subclasses of ConfigEntityBase.
+      [[], 'Drupal\system\Entity\Action'],
+      [[], 'Drupal\block\Entity\Block'],
+      [[], 'Drupal\block_content\Entity\BlockContentType'],
+      [[], 'Drupal\Core\Config\Entity\ConfigEntityBundleBase'],
+      [[], 'Drupal\block_content\Entity\BlockContentType'],
+      [[], 'Drupal\comment\Entity\CommentType'],
+      [[], 'Drupal\contact\Entity\ContactForm'],
+      [[], 'Drupal\node\Entity\NodeType'],
+      [[], 'Drupal\taxonomy\Entity\Vocabulary'],
+      [[], 'Drupal\language\Entity\ConfigurableLanguage'],
+      [[], 'Drupal\language\Entity\ContentLanguageSettings'],
+      [['id', 'label'], 'Drupal\Core\Datetime\Entity\DateFormat'],
+      [[], 'Drupal\editor\Entity\Editor'],
+      [[], 'Drupal\Core\Entity\EntityDisplayBase'],
+      [[], 'Drupal\Core\Entity\Entity\EntityFormDisplay'],
+      [[], 'Drupal\Core\Entity\Entity\EntityViewDisplay'],
+      [[], 'Drupal\Core\Entity\EntityDisplayModeBase'],
+      [[], 'Drupal\Core\Entity\Entity\EntityFormMode'],
+      [[], 'Drupal\Core\Entity\Entity\EntityViewMode'],
+      [['default_value'], 'Drupal\Core\Field\FieldConfigBase'],
+      [['default_value'], 'Drupal\Core\Field\Entity\BaseFieldOverride'],
+      [['default_value'], 'Drupal\field\Entity\FieldConfig'],
+      [[], 'Drupal\field\Entity\FieldStorageConfig'],
+      [[], 'Drupal\filter\Entity\FilterFormat'],
+      [[], 'Drupal\image\Entity\ImageStyle'],
+      [[], 'Drupal\system\Entity\Menu'],
+      [[], 'Drupal\migrate\Entity\Migration'],
+      [['load'], 'Drupal\migrate_drupal\Entity\Migration'],
+      [[], 'Drupal\rdf\Entity\RdfMapping'],
+      [[], 'Drupal\responsive_image\Entity\ResponsiveImageStyle'],
+      [[], 'Drupal\user\Entity\Role'],
+      [[], 'Drupal\search\Entity\SearchPage'],
+      [[], 'Drupal\shortcut\Entity\ShortcutSet'],
+      [[], 'Drupal\tour\Entity\Tour'],
+      [[], 'Drupal\views\Entity\View'],
+    ];
+  }
+
+  /**
+   * Tests visibility of properties.
+   *
+   * Checks that all variables of subclasses of ContentEntityBase
+   * and ConfigEntityBase are protected or are allowed to be public.
+   *
+   * @dataProvider providerEntityVariablesAreProtectedOrAreAllowedPublic
+   */
+  public function testEntityVariablesAreProtectedOrAreAllowedPublic($expected, $class) {
+    // The function get_class_vars only returns the public class variables.
+    $variables = get_class_vars($class);
+    if (($variables !== FALSE) && !empty($variables) && is_array($variables)) {
+      foreach (array_keys($variables) as $variable) {
+        $this->assertTrue(in_array($variable, $expected), 'The variable ' . $class . '::' . $variable . ' is allowed to be public.');
+      }
+    }
+  }
+}
