diff --git a/src/Configuration/ResourceConfig.php b/src/Configuration/ResourceConfig.php
index 06d7941..4bc998f 100644
--- a/src/Configuration/ResourceConfig.php
+++ b/src/Configuration/ResourceConfig.php
@@ -1,10 +1,10 @@
 <?php
 
 namespace Drupal\jsonapi\Configuration;
+
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 
-
 /**
  * Class ResourceConfig.
  *
diff --git a/src/Configuration/ResourceManager.php b/src/Configuration/ResourceManager.php
index 396dd18..50824bf 100644
--- a/src/Configuration/ResourceManager.php
+++ b/src/Configuration/ResourceManager.php
@@ -64,7 +64,8 @@ class ResourceManager implements ResourceManagerInterface {
     if ($this->all) {
       return $this->all;
     }
-    foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_definition) {
+    $entity_type_ids = array_keys($this->entityTypeManager->getDefinitions());
+    foreach ($entity_type_ids as $entity_type_id) {
       // Add a ResourceConfig per bundle.
       $this->all = array_merge($this->all, array_map(function ($bundle) use ($entity_type_id) {
         $resource_config = new ResourceConfig($this->configFactory, $this->entityTypeManager);
diff --git a/src/Query/QueryBuilder.php b/src/Query/QueryBuilder.php
index 8a90a23..88fae18 100644
--- a/src/Query/QueryBuilder.php
+++ b/src/Query/QueryBuilder.php
@@ -20,7 +20,7 @@ class QueryBuilder implements QueryBuilderInterface {
   /**
    * The options to build with which to build a query.
    */
-  protected $options;
+  protected $options = [];
 
   /**
    * The entity type manager.
diff --git a/src/Resource/EntityResource.php b/src/Resource/EntityResource.php
index d116cfe..d13b73a 100644
--- a/src/Resource/EntityResource.php
+++ b/src/Resource/EntityResource.php
@@ -88,6 +88,12 @@ class EntityResource implements EntityResourceInterface {
     $storage = $this->entityTypeManager->getStorage($entity_type_id);
     $entity_collection = new EntityCollection($storage->loadMultiple($results));
     $response = $this->buildWrappedResponse($entity_collection);
+
+    // When a new change to any entity in the resource happens, we cannot ensure
+    // the validity of this cached list. Add the list tag to deal with that.
+    $list_tag = $this->entityTypeManager->getDefinition($entity_type_id)->getListCacheTags();
+    $response->getCacheableMetadata()->setCacheTags($list_tag);
+    // Add a cache tag for every entity in the list.
     foreach ($entity_collection as $entity) {
       $this->addCacheabilityMetadata($response, $entity);
     }
diff --git a/src/Routing/Routes.php b/src/Routing/Routes.php
index 2da0aaa..e73c760 100644
--- a/src/Routing/Routes.php
+++ b/src/Routing/Routes.php
@@ -24,13 +24,6 @@ class Routes implements ContainerInjectionInterface {
   const FRONT_CONTROLLER = '\Drupal\jsonapi\RequestHandler::handle';
 
   /**
-   * The entity type manager object.
-   *
-   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
-   */
-  protected $entityTypeManager;
-
-  /**
    * The resource manager interface.
    *
    * @var \Drupal\jsonapi\Configuration\ResourceManagerInterface
@@ -45,8 +38,7 @@ class Routes implements ContainerInjectionInterface {
    * @param \Drupal\jsonapi\Configuration\ResourceManagerInterface $resource_manager
    *   The resource manager.
    */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager, ResourceManagerInterface $resource_manager) {
-    $this->entityTypeManager = $entity_type_manager;
+  public function __construct(ResourceManagerInterface $resource_manager) {
     $this->resourceManager = $resource_manager;
   }
 
@@ -54,11 +46,9 @@ class Routes implements ContainerInjectionInterface {
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
-    /* @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
-    $entity_type_manager = $container->get('entity_type.manager');
     /* @var \Drupal\jsonapi\Configuration\ResourceManagerInterface $resource_manager */
     $resource_manager = $container->get('jsonapi.resource.manager');
-    return new static($entity_type_manager, $resource_manager);
+    return new static($resource_manager);
   }
 
   /**
diff --git a/tests/src/Kernel/Configuration/ResourceManagerTest.php b/tests/src/Kernel/Configuration/ResourceManagerTest.php
new file mode 100644
index 0000000..76c9a3b
--- /dev/null
+++ b/tests/src/Kernel/Configuration/ResourceManagerTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Drupal\Tests\jsonapi\Kernel\Configuration;
+
+use Drupal\jsonapi\Configuration\ResourceConfigInterface;
+use Drupal\jsonapi\Configuration\ResourceManager;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\node\Entity\NodeType;
+
+/**
+ * Class ResourceManagerTest.
+ *
+ * @package Drupal\Tests\jsonapi\Kernel\Resource
+ *
+ * @coversDefaultClass \Drupal\jsonapi\Configuration\ResourceManager
+ *
+ * @group jsonapi
+ */
+class ResourceManagerTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'node',
+    'jsonapi',
+    'rest',
+    'serialization',
+    'system',
+    'user',
+  ];
+
+  /**
+   * The entity resource under test.
+   *
+   * @var \Drupal\jsonapi\Configuration\ResourceManagerInterface
+   */
+  protected $resourceManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    // Add the entity schemas.
+    $this->installEntitySchema('node');
+    $this->installEntitySchema('user');
+    // Add the additional table schemas.
+    $this->installSchema('system', ['sequences']);
+    $this->installSchema('node', ['node_access']);
+    $this->installSchema('user', ['users_data']);
+    NodeType::create([
+      'type' => 'article',
+    ])->save();
+    NodeType::create([
+      'type' => 'page',
+    ])->save();
+
+    $this->resourceManager = $this->container->get('jsonapi.resource.manager');
+  }
+
+  /**
+   * @covers ::all
+   */
+  public function testAll() {
+    // Make sure that there are resources being created.
+    $all = $this->resourceManager->all();
+    $this->assertNotEmpty($all);
+    // Get a random resource config.
+    $resource_config = $all[mt_rand(0, count($all) - 1)];
+    $this->assertNotEmpty($resource_config->getDeserializationTargetClass());
+    $this->assertNotEmpty($resource_config->getEntityTypeId());
+    $this->assertNotEmpty($resource_config->getBundleId());
+    $this->assertNotEmpty($resource_config->getGlobalConfig());
+    $this->assertNotEmpty($resource_config->getPath());
+    $this->assertNotEmpty($resource_config->getTypeName());
+  }
+
+  /**
+   * @covers ::get
+   */
+  public function testGet() {
+    // Make sure that there are resources being created.
+    $resource_config = $this->resourceManager->get('node', 'article');
+    $this->assertInstanceOf(ResourceConfigInterface::class, $resource_config);
+    $this->assertSame('Drupal\node\Entity\Node', $resource_config->getDeserializationTargetClass());
+    $this->assertSame('node', $resource_config->getEntityTypeId());
+    $this->assertSame('article', $resource_config->getBundleId());
+    $this->assertNotEmpty($resource_config->getGlobalConfig());
+    $this->assertSame('/article', $resource_config->getPath());
+    $this->assertSame('article', $resource_config->getTypeName());
+  }
+
+}
diff --git a/tests/src/Kernel/Resource/EntityResourceTest.php b/tests/src/Kernel/Resource/EntityResourceTest.php
new file mode 100644
index 0000000..7adff5a
--- /dev/null
+++ b/tests/src/Kernel/Resource/EntityResourceTest.php
@@ -0,0 +1,136 @@
+<?php
+
+namespace Drupal\Tests\jsonapi\Kernel\Resource;
+
+use Drupal\jsonapi\EntityCollection;
+use Drupal\jsonapi\Resource\DocumentWrapper;
+use Drupal\jsonapi\Resource\EntityResource;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\node\Entity\Node;
+use Drupal\node\Entity\NodeType;
+use Drupal\user\Entity\Role;
+use Drupal\user\Entity\User;
+use Drupal\user\RoleInterface;
+use Symfony\Component\HttpFoundation\ParameterBag;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class EntityResourceTest.
+ *
+ * @package Drupal\Tests\jsonapi\Kernel\Resource
+ *
+ * @coversDefaultClass \Drupal\jsonapi\Resource\EntityResource
+ * @group jsonapi
+ */
+class EntityResourceTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'node',
+    'jsonapi',
+    'rest',
+    'serialization',
+    'system',
+    'user',
+  ];
+
+  /**
+   * The entity resource under test.
+   *
+   * @var \Drupal\jsonapi\Resource\EntityResource
+   */
+  protected $entityResource;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    // Add the entity schemas.
+    $this->installEntitySchema('node');
+    $this->installEntitySchema('user');
+    // Add the additional table schemas.
+    $this->installSchema('system', ['sequences']);
+    $this->installSchema('node', ['node_access']);
+    $this->installSchema('user', ['users_data']);
+    $type = NodeType::create([
+      'type' => 'article',
+    ]);
+    $type->save();
+    $this->user = User::create([
+      'name' => 'user1',
+      'mail' => 'user@localhost',
+    ]);
+    $this->user->save();
+    $this->node = Node::create([
+      'title' => 'dummy_title',
+      'type' => 'article',
+      'uid' => $this->user->id(),
+    ]);
+
+    $this->node->save();
+
+    // Give anonymous users permission to view user profiles, so that we can
+    // verify the cache tags of cached versions of user profile pages.
+    Role::create([
+      'id' => RoleInterface::ANONYMOUS_ID,
+      'permissions' => [
+        'access user profiles',
+        'access content',
+      ],
+    ])->save();
+
+    $this->entityResource = new EntityResource(
+      $this->container->get('jsonapi.resource.manager')->get('node', 'article'),
+      $this->container->get('entity_type.manager'),
+      $this->container->get('jsonapi.query_builder'),
+      $this->container->get('entity_field.manager')
+    );
+
+  }
+
+
+  /**
+   * @covers ::getIndividual
+   */
+  public function testGetIndividual() {
+    $response = $this->entityResource->getIndividual($this->node);
+    $this->assertInstanceOf(DocumentWrapper::class, $response->getResponseData());
+    $this->assertEquals(1, $response->getResponseData()->getData()->id());
+    $this->assertSame('node:1', $response->getCacheableMetadata()->getCacheTags()[0]);
+  }
+
+  /**
+   * @covers ::getIndividual
+   * @expectedException \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+   */
+  public function testGetIndividualDenied() {
+    $role = Role::load(RoleInterface::ANONYMOUS_ID);
+    $role->revokePermission('access content');
+    $role->save();
+    $this->entityResource->getIndividual($this->node);
+  }
+
+  /**
+   * @covers ::getCollection
+   */
+  public function testGetCollection() {
+    // Fake the request.
+    $request = $this->prophesize(Request::class);
+    $params = $this->prophesize(ParameterBag::class);
+    $params->get('_route_params')->willReturn(['_json_api_params' => []]);
+    $request->attributes = $params->reveal();
+
+    // Get the response.
+    $response = $this->entityResource->getCollection($request->reveal());
+
+    // Assertions.
+    $this->assertInstanceOf(DocumentWrapper::class, $response->getResponseData());
+    $this->assertInstanceOf(EntityCollection::class, $response->getResponseData()->getData());
+    $this->assertEquals(1, $response->getResponseData()->getData()->getIterator()->current()->id());
+    $this->assertEquals(['node:1', 'node_list'], $response->getCacheableMetadata()->getCacheTags());
+  }
+
+}
diff --git a/tests/src/Unit/Routing/RoutesTest.php b/tests/src/Unit/Routing/RoutesTest.php
new file mode 100644
index 0000000..f5904aa
--- /dev/null
+++ b/tests/src/Unit/Routing/RoutesTest.php
@@ -0,0 +1,136 @@
+<?php
+
+namespace Drupal\Tests\jsonapi\Unit\Routing;
+
+use Drupal\Core\Config\ImmutableConfig;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\jsonapi\Configuration\ResourceConfigInterface;
+use Drupal\jsonapi\Configuration\ResourceManagerInterface;
+use Drupal\jsonapi\Routing\Routes;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Class RoutesTest.
+ *
+ * @package Drupal\Tests\jsonapi\Unit\Routing
+ *
+ * @coversDefaultClass \Drupal\jsonapi\Routing\Routes
+ * @group jsonapi
+ */
+class RoutesTest extends UnitTestCase {
+
+  /**
+   * List of routes objects for the different scenarios.
+   *
+   * @var Routes[]
+   */
+  protected $routes;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    // Mock the resource manager to have some resources available.
+    $resource_manager = $this->prophesize(ResourceManagerInterface::class);
+
+    // Create some resource mocks for the manager.
+    $resource_config = $this->prophesize(ResourceConfigInterface::class);
+    $global_config = $this->prophesize(ImmutableConfig::class);
+    $global_config->get('prefix')->willReturn('api');
+    $resource_config->getGlobalConfig()->willReturn($global_config->reveal());
+    $resource_config->getEntityTypeId()->willReturn('entity_type_1');
+    $resource_config->getBundleId()->willReturn('bundle_1_1');
+    // Make sure that we're not coercing the bundle into the path, they can be
+    // different in the future.
+    $resource_config->getPath()->willReturn('/bundle_path_1');
+    $resource_config->getTypeName()->willReturn('resource_type_1');
+    $resource_config->getDeserializationTargetClass()->willReturn('\Drupal\jsonapi\EntityType1');
+    $resource_manager->all()->willReturn([$resource_config->reveal()]);
+    $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
+    $container = $this->prophesize(ContainerInterface::class);
+    $container->get('jsonapi.resource.manager')->willReturn($resource_manager->reveal());
+
+    $this->routes['ok'] = Routes::create($container->reveal());
+  }
+
+
+  /**
+   * @covers ::routes
+   */
+  public function testRoutesCollection() {
+    // Get the route collection and start making assertions.
+    $routes = $this->routes['ok']->routes();
+
+    // Make sure that there are 4 routes for each resource.
+    $this->assertEquals(4, $routes->count());
+
+    $iterator = $routes->getIterator();
+    // Check the collection route.
+    /** @var \Symfony\Component\Routing\Route $route */
+    $route = $iterator->offsetGet('api.dynamic.resource_type_1.collection');
+    $this->assertSame('/api/bundle_path_1', $route->getPath());
+    $this->assertSame('entity_type_1', $route->getRequirement('_entity_type'));
+    $this->assertSame('bundle_1_1', $route->getRequirement('_bundle'));
+    $this->assertEquals(['GET', 'POST'], $route->getMethods());
+    $this->assertSame('\Drupal\jsonapi\RequestHandler::handle', $route->getDefault('_controller'));
+    $this->assertSame('\Drupal\jsonapi\EntityType1', $route->getOption('serialization_class'));
+  }
+
+  /**
+   * @covers ::routes
+   */
+  public function testRoutesIndividual() {
+    // Get the route collection and start making assertions.
+    $iterator = $this->routes['ok']->routes()->getIterator();
+
+    // Check the individual route.
+    /** @var \Symfony\Component\Routing\Route $route */
+    $route = $iterator->offsetGet('api.dynamic.resource_type_1.individual');
+    $this->assertSame('/api/bundle_path_1/{entity_type_1}', $route->getPath());
+    $this->assertSame('entity_type_1', $route->getRequirement('_entity_type'));
+    $this->assertSame('bundle_1_1', $route->getRequirement('_bundle'));
+    $this->assertEquals(['GET', 'PATCH', 'DELETE'], $route->getMethods());
+    $this->assertSame('\Drupal\jsonapi\RequestHandler::handle', $route->getDefault('_controller'));
+    $this->assertSame('\Drupal\jsonapi\EntityType1', $route->getOption('serialization_class'));
+    $this->assertEquals(['entity_type_1' => ['type' => 'entity:entity_type_1']], $route->getOption('parameters'));
+  }
+
+  /**
+   * @covers ::routes
+   */
+  public function testRoutesRelated() {
+    // Get the route collection and start making assertions.
+    $iterator = $this->routes['ok']->routes()->getIterator();
+
+    // Check the related route.
+    /** @var \Symfony\Component\Routing\Route $route */
+    $route = $iterator->offsetGet('api.dynamic.resource_type_1.related');
+    $this->assertSame('/api/bundle_path_1/{entity_type_1}/{related}', $route->getPath());
+    $this->assertSame('entity_type_1', $route->getRequirement('_entity_type'));
+    $this->assertSame('bundle_1_1', $route->getRequirement('_bundle'));
+    $this->assertEquals(['GET'], $route->getMethods());
+    $this->assertSame('\Drupal\jsonapi\RequestHandler::handle', $route->getDefault('_controller'));
+    $this->assertEquals(['entity_type_1' => ['type' => 'entity:entity_type_1']], $route->getOption('parameters'));
+  }
+
+  /**
+   * @covers ::routes
+   */
+  public function testRoutesRelationships() {
+    // Get the route collection and start making assertions.
+    $iterator = $this->routes['ok']->routes()->getIterator();
+
+    // Check the relationships route.
+    /** @var \Symfony\Component\Routing\Route $route */
+    $route = $iterator->offsetGet('api.dynamic.resource_type_1.relationship');
+    $this->assertSame('/api/bundle_path_1/{entity_type_1}/relationships/{related}', $route->getPath());
+    $this->assertSame('entity_type_1', $route->getRequirement('_entity_type'));
+    $this->assertSame('bundle_1_1', $route->getRequirement('_bundle'));
+    $this->assertEquals(['GET', 'POST', 'DELETE'], $route->getMethods());
+    $this->assertSame('\Drupal\jsonapi\RequestHandler::handle', $route->getDefault('_controller'));
+    $this->assertEquals(['entity_type_1' => ['type' => 'entity:entity_type_1']], $route->getOption('parameters'));
+  }
+
+}
