diff --git a/README.md b/README.md
index 38404a4f50a..fcea8d91d33 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 # Google Cloud Node.js Client
-> Node idiomatic client for Google Cloud services.
+> Node.js idiomatic client for Google Cloud services.
 
 [![NPM Version](https://img.shields.io/npm/v/gcloud.svg)](https://www.npmjs.org/package/gcloud)
 [![Travis Build Status](https://travis-ci.org/GoogleCloudPlatform/gcloud-node.svg)](https://travis-ci.org/GoogleCloudPlatform/gcloud-node/)
@@ -65,7 +65,7 @@ dataset = new datastore.Dataset({
   keyFilename: '/path/to/keyfile.json'
 });
 
-dataset.get(dataset.key('Product', 'Computer'), function(err, entity) {
+dataset.get(dataset.key(['Product', 'Computer']), function(err, entity) {
   console.log(err || entity);
 });
 ```
diff --git a/lib/datastore/dataset.js b/lib/datastore/dataset.js
index 5ef2b8609b9..35290fc1367 100644
--- a/lib/datastore/dataset.js
+++ b/lib/datastore/dataset.js
@@ -111,24 +111,37 @@ function Dataset(options) {
  *
  * You may also specify a configuration object to define a namespace and path.
  *
+ * @param {...*=} options - Key path. To specify or override a namespace,
+ *     you must use an object here to explicitly state it.
+ * @param {object=} options - Configuration object.
+ * @param {...*=} options.path - Key path.
+ * @param {string=} options.namespace - Optional namespace.
+ *
  * @example
- * // Create a key from the dataset's namespace.
- * var company123 = dataset.key('Company', 123);
+ * var key;
+ *
+ * // Create an incomplete key from the dataset namespace, kind='Company'
+ * key = dataset.key('Company');
+ *
+ * // A complete key from the dataset namespace, kind='Company', id=123
+ * key = dataset.key(['Company', 123]);
  *
- * // Create a key from a provided namespace and path.
- * var nsCompany123 = dataset.key({
+ * // A complete key from the dataset namespace, kind='Company', name='Google'
+ * // Note: `id` is used for numeric identifiers and `name` is used otherwise
+ * key = dataset.key(['Company', 'Google']);
+ *
+ * // A complete key from a provided namespace and path.
+ * key = dataset.key({
  *   namespace: 'My-NS',
  *   path: ['Company', 123]
  * });
  */
-Dataset.prototype.key = function(keyConfig) {
-  if (!util.is(keyConfig, 'object')) {
-    keyConfig = {
-      namespace: this.namespace,
-      path: util.toArray(arguments)
-    };
-  }
-  return new entity.Key(keyConfig);
+Dataset.prototype.key = function(options) {
+  options = util.is(options, 'object') ? options : {
+    namespace: this.namespace,
+    path: util.arrayize(options)
+  };
+  return new entity.Key(options);
 };
 
 
@@ -165,8 +178,8 @@ Dataset.prototype.createQuery = function(namespace, kinds) {
  *
  * @example
  * dataset.get([
- *   dataset.key('Company', 123),
- *   dataset.key('Product', 'Computer')
+ *   dataset.key(['Company', 123]),
+ *   dataset.key(['Product', 'Computer'])
  * ], function(err, entities) {});
  */
 Dataset.prototype.get = function(key, callback) {
@@ -188,7 +201,7 @@ Dataset.prototype.get = function(key, callback) {
  * @example
  * // Save a single entity.
  * dataset.save({
- *   key: dataset.key('Company', null),
+ *   key: dataset.key('Company'),
  *   data: {
  *     rating: '10'
  *   }
@@ -200,13 +213,13 @@ Dataset.prototype.get = function(key, callback) {
  * // Save multiple entities at once.
  * dataset.save([
  *   {
- *     key: dataset.key('Company', 123),
+ *     key: dataset.key(['Company', 123]),
  *     data: {
  *       HQ: 'Dallas, TX'
  *     }
  *   },
  *   {
- *     key: dataset.key('Product', 'Computer'),
+ *     key: dataset.key(['Product', 'Computer']),
  *     data: {
  *       vendor: 'Dell'
  *     }
@@ -228,12 +241,12 @@ Dataset.prototype.save = function(key, obj, callback) {
  *
  * @example
  * // Delete a single entity.
- * dataset.delete(dataset.key('Company', 123), function(err) {});
+ * dataset.delete(dataset.key(['Company', 123]), function(err) {});
  *
  * // Delete multiple entities at once.
  * dataset.delete([
- *   dataset.key('Company', 123),
- *   dataset.key('Product', 'Computer')
+ *   dataset.key(['Company', 123]),
+ *   dataset.key(['Product', 'Computer'])
  * ], function(err) {});
  */
 Dataset.prototype.delete = function(key, callback) {
@@ -277,7 +290,7 @@ Dataset.prototype.runQuery = function(q, callback) {
  * dataset.runInTransaction(function(transaction, done) {
  *   // From the `transaction` object, execute dataset methods as usual.
  *   // Call `done` when you're ready to commit all of the changes.
- *   transaction.get(dataset.key('Company', 123), function(err, entity) {
+ *   transaction.get(dataset.key(['Company', 123]), function(err, entity) {
  *     if (err) {
  *       transaction.rollback(done);
  *       return;
@@ -308,14 +321,17 @@ Dataset.prototype.runInTransaction = function(fn, callback) {
  * @example
  * // The following call will create 100 new IDs from the Company kind, which
  * // exists under the default namespace.
- * var incompleteKey = dataset.key('Company', null);
+ * var incompleteKey = dataset.key('Company');
  * dataset.allocateIds(incompleteKey, 100, function(err, keys) {});
  *
  * // You may prefer to create IDs from a non-default namespace by providing an
  * // incomplete key with a namespace. Similar to the previous example, the call
  * // below will create 100 new IDs, but from the Company kind that exists under
  * // the "ns-test" namespace.
- * var incompleteKey = dataset.key('ns-test', 'Company', null);
+ * var incompleteKey = dataset.key({
+ *   namespace: 'ns-test',
+ *   path: ['Company']
+ * });
  * dataset.allocateIds(incompleteKey, 100, function(err, keys) {});
  */
 Dataset.prototype.allocateIds = function(incompleteKey, n, callback) {
diff --git a/lib/datastore/entity.js b/lib/datastore/entity.js
index 6c003c90f1d..931c213a40e 100644
--- a/lib/datastore/entity.js
+++ b/lib/datastore/entity.js
@@ -197,13 +197,21 @@ function keyFromKeyProto(proto) {
   var keyOptions = {
     path: []
   };
+
   if (proto.partition_id && proto.partition_id.namespace) {
     keyOptions.namespace = proto.partition_id.namespace;
   }
-  proto.path_element.forEach(function(path) {
+
+  proto.path_element.forEach(function(path, index) {
+    var id = Number(path.id) || path.name;
     keyOptions.path.push(path.kind);
-    keyOptions.path.push(Number(path.id) || path.name || null);
+    if (id) {
+      keyOptions.path.push(id);
+    } else if (index < proto.path_element.length - 1) {
+      throw new Error('Invalid key. Ancestor keys require an id or name.');
+    }
   });
+
   return new Key(keyOptions);
 }
 
@@ -216,7 +224,7 @@ module.exports.keyFromKeyProto = keyFromKeyProto;
  * @return {object}
  *
  * @example
- * var keyProto = keyToKeyProto(new Key('Company', 1));
+ * var keyProto = keyToKeyProto(new Key(['Company', 1]));
  *
  * // keyProto:
  * // {
@@ -230,8 +238,8 @@ module.exports.keyFromKeyProto = keyFromKeyProto;
  */
 function keyToKeyProto(key) {
   var keyPath = key.path;
-  if (keyPath.length < 2) {
-    throw new Error('A key should contain at least a kind and an identifier.');
+  if (keyPath.length === 0) {
+    throw new Error('A key should contain at least a kind.');
   }
   var path = [];
   for (var i = 0; i < keyPath.length; i += 2) {
@@ -244,6 +252,8 @@ function keyToKeyProto(key) {
       } else {
         p.id = val;
       }
+    } else if (i < keyPath.length - 2) { // i is second last path item
+      throw new Error('Invalid key. Ancestor keys require an id or name.');
     }
     path.push(p);
   }
@@ -300,8 +310,8 @@ module.exports.formatArray = formatArray;
  * @return {boolean}
  *
  * @example
- * isKeyComplete(new Key('Company', 'Google')); // true
- * isKeyComplete(new Key('Company', null)); // false
+ * isKeyComplete(new Key(['Company', 'Google'])); // true
+ * isKeyComplete(new Key('Company')); // false
  */
 module.exports.isKeyComplete = function(key) {
   var proto = keyToKeyProto(key);
diff --git a/lib/datastore/query.js b/lib/datastore/query.js
index 4258c977110..8166791825a 100644
--- a/lib/datastore/query.js
+++ b/lib/datastore/query.js
@@ -96,7 +96,7 @@ function Query(namespace, kinds) {
  *
  * // To filter by key, use `__key__` for the property name. Filter on keys
  * // stored as properties is not currently supported.
- * var keyQuery = query.filter('__key__ =', dataset.key('Company', 'Google'));
+ * var keyQuery = query.filter('__key__ =', dataset.key(['Company', 'Google']));
  */
 Query.prototype.filter = function(filter, value) {
   // TODO: Add filter validation.
@@ -122,7 +122,7 @@ Query.prototype.filter = function(filter, value) {
  * @return {module:datastore/query}
  *
  * @example
- * var ancestoryQuery = query.hasAncestor(dataset.key('Parent', 123));
+ * var ancestoryQuery = query.hasAncestor(dataset.key(['Parent', 123]));
  */
 Query.prototype.hasAncestor = function(key) {
   var query = extend(new Query(), this);
diff --git a/lib/datastore/transaction.js b/lib/datastore/transaction.js
index 6c1f512a129..9736d6da669 100644
--- a/lib/datastore/transaction.js
+++ b/lib/datastore/transaction.js
@@ -80,7 +80,7 @@ function Transaction(conn, datasetId) {
  * @example
  * transaction.begin(function(err) {
  *   // Perform Datastore operations as usual.
- *   transaction.get(dataset.key('Company', 123), function(err, entity) {
+ *   transaction.get(dataset.key(['Company', 123]), function(err, entity) {
  *     // Commit the transaction.
  *     transaction.finalize(function(err) {});
  *
@@ -189,20 +189,19 @@ Transaction.prototype.finalize = function(callback) {
  * transaction. Get operations require a valid key to retrieve the
  * key-identified entity from Datastore.
  *
- * @param {Key|Key[]} key -
- *     Datastore key object(s).
+ * @param {Key|Key[]} key - Datastore key object(s).
  * @param {function} callback - The callback function.
  *
  * @example
  * // These examples work with both a Transaction object and a Dataset object.
  *
  * // Get a single entity.
- * transaction.get(dataset.key('Company', 123), function(err, entity));
+ * transaction.get(dataset.key(['Company', 123]), function(err, entity) {});
  *
  * // Get multiple entities at once.
  * transaction.get([
- *   dataset.key('Company', 123),
- *   dataset.key('Product', 'Computer')
+ *   dataset.key(['Company', 123]),
+ *   dataset.key(['Product', 'Computer'])
  * ], function(err, entities) {});
  */
 Transaction.prototype.get = function(keys, callback) {
@@ -256,7 +255,7 @@ Transaction.prototype.get = function(keys, callback) {
  *
  * // Save a single entity.
  * transaction.save({
- *   key: dataset.key('Company', null),
+ *   key: dataset.key('Company'),
  *   data: {
  *     rating: '10'
  *   }
@@ -268,13 +267,13 @@ Transaction.prototype.get = function(keys, callback) {
  * // Save multiple entities at once.
  * transaction.save([
  *   {
- *     key: dataset.key('Company', 123),
+ *     key: dataset.key(['Company', 123]),
  *     data: {
  *       HQ: 'Dallas, TX'
  *     }
  *   },
  *   {
- *     key: dataset.key('Product', 'Computer'),
+ *     key: dataset.key(['Product', 'Computer']),
  *     data: {
  *       vendor: 'Dell'
  *     }
@@ -332,12 +331,12 @@ Transaction.prototype.save = function(entities, callback) {
  * // These examples work with both a Transaction object and a Dataset object.
  *
  * // Delete a single entity.
- * transaction.delete(dataset.key('Company', 123), function(err) {});
+ * transaction.delete(dataset.key(['Company', 123]), function(err) {});
  *
  * // Delete multiple entities at once.
  * transaction.delete([
- *   dataset.key('Company', 123),
- *   dataset.key('Product', 'Computer')
+ *   dataset.key(['Company', 123]),
+ *   dataset.key(['Product', 'Computer'])
  * ], function(err) {});
  */
 Transaction.prototype.delete = function(keys, callback) {
diff --git a/regression/datastore.js b/regression/datastore.js
index 0179bdacd71..e81a163b0f3 100644
--- a/regression/datastore.js
+++ b/regression/datastore.js
@@ -28,7 +28,7 @@ var entity = require('../lib/datastore/entity.js');
 describe('datastore', function() {
 
   it('should allocate IDs', function(done) {
-    ds.allocateIds(ds.key('Kind', null), 10, function(err, keys) {
+    ds.allocateIds(ds.key('Kind'), 10, function(err, keys) {
       assert.ifError(err);
       assert.equal(keys.length, 10);
       assert.equal(entity.isKeyComplete(keys[0]), true);
@@ -48,7 +48,7 @@ describe('datastore', function() {
     };
 
     it('should save/get/delete with a key name', function(done) {
-      var postKey = ds.key('Post', 'post1');
+      var postKey = ds.key(['Post', 'post1']);
       ds.save({ key: postKey, data: post }, function(err, key) {
         assert.ifError(err);
         assert.equal(key.path[1], 'post1');
@@ -64,7 +64,7 @@ describe('datastore', function() {
     });
 
     it('should save/get/delete with a numeric key id', function(done) {
-      var postKey = ds.key('Post', 123456789);
+      var postKey = ds.key(['Post', 123456789]);
       ds.save({
         key: postKey,
         data: post
@@ -84,16 +84,16 @@ describe('datastore', function() {
 
     it('should save/get/delete with a generated key id', function(done) {
       ds.save({
-        key: ds.key('Post', null),
+        key: ds.key('Post'),
         data: post
       }, function(err, key) {
         assert.ifError(err);
         var assignedId = key.path[1];
         assert(assignedId);
-        ds.get(ds.key('Post', assignedId), function(err, entity) {
+        ds.get(ds.key(['Post', assignedId]), function(err, entity) {
           assert.ifError(err);
           assert.deepEqual(entity.data, post);
-          ds.delete(ds.key('Post', assignedId), function(err) {
+          ds.delete(ds.key(['Post', assignedId]), function(err) {
             assert.ifError(err);
             done();
           });
@@ -111,15 +111,15 @@ describe('datastore', function() {
         wordCount: 450,
         rating: 4.5,
       };
-      var key = ds.key('Post', null);
+      var key = ds.key('Post');
       ds.save([
         { key: key, data: post },
         { key: key, data: post2 }
       ], function(err, keys) {
         assert.ifError(err);
         assert.equal(keys.length,2);
-        var firstKey = ds.key('Post', keys[0].path[1]);
-        var secondKey = ds.key('Post', keys[1].path[1]);
+        var firstKey = ds.key(['Post', keys[0].path[1]]);
+        var secondKey = ds.key(['Post', keys[1].path[1]]);
         ds.get([firstKey, secondKey], function(err, entities) {
           assert.ifError(err);
           assert.equal(entities.length, 2);
@@ -135,7 +135,7 @@ describe('datastore', function() {
 
   it('should be able to save keys as a part of entity and query by key',
       function(done) {
-    var personKey = ds.key('Person', 'name');
+    var personKey = ds.key(['Person', 'name']);
     ds.save({
       key: personKey,
       data: {
@@ -158,14 +158,14 @@ describe('datastore', function() {
   describe('querying the datastore', function() {
 
     var keys = [
-      ds.key('Character', 'Rickard'),
-      ds.key('Character', 'Rickard', 'Character', 'Eddard'),
-      ds.key('Character', 'Catelyn'),
-      ds.key('Character', 'Eddard', 'Character', 'Arya'),
-      ds.key('Character', 'Eddard', 'Character', 'Sansa'),
-      ds.key('Character', 'Eddard', 'Character', 'Robb'),
-      ds.key('Character', 'Eddard', 'Character', 'Bran'),
-      ds.key('Character', 'Eddard', 'Character', 'Jon Snow')
+      ds.key(['Character', 'Rickard']),
+      ds.key(['Character', 'Rickard', 'Character', 'Eddard']),
+      ds.key(['Character', 'Catelyn']),
+      ds.key(['Character', 'Eddard', 'Character', 'Arya']),
+      ds.key(['Character', 'Eddard', 'Character', 'Sansa']),
+      ds.key(['Character', 'Eddard', 'Character', 'Robb']),
+      ds.key(['Character', 'Eddard', 'Character', 'Bran']),
+      ds.key(['Character', 'Eddard', 'Character', 'Jon Snow'])
     ];
 
     var characters = [{
@@ -265,7 +265,7 @@ describe('datastore', function() {
 
     it('should filter by ancestor', function(done) {
       var q = ds.createQuery('Character')
-          .hasAncestor(ds.key('Character', 'Eddard'));
+          .hasAncestor(ds.key(['Character', 'Eddard']));
       ds.runQuery(q, function(err, entities) {
         assert.ifError(err);
         assert.equal(entities.length, 5);
@@ -275,7 +275,7 @@ describe('datastore', function() {
 
     it('should filter by key', function(done) {
       var q = ds.createQuery('Character')
-          .filter('__key__ =', ds.key('Character', 'Rickard'));
+          .filter('__key__ =', ds.key(['Character', 'Rickard']));
       ds.runQuery(q, function(err, entities) {
         assert.ifError(err);
         assert.equal(entities.length, 1);
@@ -370,7 +370,7 @@ describe('datastore', function() {
   describe('transactions', function() {
 
     it('should run in a transaction', function(done) {
-      var key = ds.key('Company', 'Google');
+      var key = ds.key(['Company', 'Google']);
       var obj = {
         url: 'www.google.com'
       };
diff --git a/test/datastore/dataset.js b/test/datastore/dataset.js
index ae2325e9bb5..7b8695f45af 100644
--- a/test/datastore/dataset.js
+++ b/test/datastore/dataset.js
@@ -53,21 +53,43 @@ describe('Dataset', function() {
     assert.deepEqual(ds.connection.credentials, uniqueCredentials);
   });
 
-  it('should return a key scoped by namespace', function() {
-    var ds = datastore.dataset({ projectId: 'test', namespace: 'my-ns' });
-    var key = ds.key('Company', 1);
-    assert.equal(key.namespace, 'my-ns');
-    assert.deepEqual(key.path, ['Company', 1]);
-  });
+  describe('key', function() {
+    it('should return key scoped by default namespace', function() {
+      var ds = datastore.dataset({ projectId: 'test', namespace: 'my-ns' });
+      var key = ds.key(['Company', 1]);
+      assert.equal(key.namespace, 'my-ns');
+      assert.deepEqual(key.path, ['Company', 1]);
+    });
 
-  it('should allow namespace specification when creating a key', function() {
-    var ds = datastore.dataset({ projectId: 'test' });
-    var key = ds.key({
-      namespace: 'custom-ns',
-      path: ['Company', 1]
+    it('should allow namespace specification', function() {
+      var ds = datastore.dataset({ projectId: 'test', namespace: 'my-ns' });
+      var key = ds.key({
+        namespace: 'custom-ns',
+        path: ['Company', 1]
+      });
+      assert.equal(key.namespace, 'custom-ns');
+      assert.deepEqual(key.path, ['Company', 1]);
+    });
+
+    it('should create incomplete key from string', function() {
+      var ds = datastore.dataset({ projectId: 'test' });
+      var key = ds.key('hello');
+      assert.deepEqual(key.path, ['hello']);
+    });
+
+    it('should create incomplete key from array in obj', function() {
+      var ds = datastore.dataset({ projectId: 'test' });
+      var key = ds.key({
+        path: ['world']
+      });
+      assert.deepEqual(key.path, ['world']);
+    });
+
+    it('should create incomplete key from array', function() {
+      var ds = datastore.dataset({ projectId: 'test' });
+      var key = ds.key(['Company']);
+      assert.deepEqual(key.path, ['Company']);
     });
-    assert.equal(key.namespace, 'custom-ns');
-    assert.deepEqual(key.path, ['Company', 1]);
   });
 
   it('should get by key', function(done) {
@@ -77,7 +99,7 @@ describe('Dataset', function() {
       assert.equal(proto.key.length, 1);
       callback(null, mockRespGet);
     };
-    ds.get(ds.key('Kind', 123), function(err, entity) {
+    ds.get(ds.key(['Kind', 123]), function(err, entity) {
       var data = entity.data;
       assert.deepEqual(entity.key.path, ['Kind', 5732568548769792]);
       assert.strictEqual(data.author, 'Silvano');
@@ -94,7 +116,7 @@ describe('Dataset', function() {
       assert.equal(proto.key.length, 1);
       callback(null, mockRespGet);
     };
-    var key = ds.key('Kind', 5732568548769792);
+    var key = ds.key(['Kind', 5732568548769792]);
     ds.get([key], function(err, entities) {
       var entity = entities[0];
       var data = entity.data;
@@ -108,8 +130,8 @@ describe('Dataset', function() {
 
   it('should continue looking for deferred results', function(done) {
     var ds = datastore.dataset({ projectId: 'test' });
-    var key = ds.key('Kind', 5732568548769792);
-    var key2 = ds.key('Kind', 5732568548769792);
+    var key = ds.key(['Kind', 5732568548769792]);
+    var key2 = ds.key(['Kind', 5732568548769792]);
     var lookupCount = 0;
     ds.transaction.makeReq = function(method, proto, typ, callback) {
       lookupCount++;
@@ -136,7 +158,7 @@ describe('Dataset', function() {
       assert.equal(!!proto.mutation.delete, true);
       callback();
     };
-    ds.delete(ds.key('Kind', 123), done);
+    ds.delete(ds.key(['Kind', 123]), done);
   });
 
   it('should multi delete by keys', function(done) {
@@ -147,8 +169,8 @@ describe('Dataset', function() {
       callback();
     };
     ds.delete([
-      ds.key('Kind', 123),
-      ds.key('Kind', 345)
+      ds.key(['Kind', 123]),
+      ds.key(['Kind', 345])
     ], done);
   });
 
@@ -159,7 +181,7 @@ describe('Dataset', function() {
       assert.equal(proto.mutation.insert_auto_id.length, 1);
       callback();
     };
-    var key = ds.key('Kind', null);
+    var key = ds.key('Kind');
     ds.save({ key: key, data: {} }, done);
   });
 
@@ -174,8 +196,8 @@ describe('Dataset', function() {
       callback();
     };
     ds.save([
-      { key: ds.key('Kind', 123), data: { k: 'v' } },
-      { key: ds.key('Kind', 456), data: { k: 'v' } }
+      { key: ds.key(['Kind', 123]), data: { k: 'v' } },
+      { key: ds.key(['Kind', 456]), data: { k: 'v' } }
     ], done);
   });
 
@@ -194,8 +216,8 @@ describe('Dataset', function() {
         ]
       });
     };
-    ds.allocateIds(ds.key('Kind', null), 1, function(err, ids) {
-      assert.deepEqual(ids[0], ds.key('Kind', 123));
+    ds.allocateIds(ds.key('Kind'), 1, function(err, ids) {
+      assert.deepEqual(ids[0], ds.key(['Kind', 123]));
       done();
     });
   });
@@ -203,7 +225,7 @@ describe('Dataset', function() {
   it('should throw if trying to allocate IDs with complete keys', function() {
     var ds = datastore.dataset({ projectId: 'test' });
     assert.throws(function() {
-      ds.allocateIds(ds.key('Kind', 123));
+      ds.allocateIds(ds.key(['Kind', 123]));
     });
   });
 
diff --git a/test/datastore/entity.js b/test/datastore/entity.js
index 2ac8fa9ea30..da2e3798a29 100644
--- a/test/datastore/entity.js
+++ b/test/datastore/entity.js
@@ -37,7 +37,7 @@ var entityProto = {
         'key_value': {
             'path_element': [{
                 'kind': 'Kind',
-                'name': 'another'
+                'name': '123'
             }]
         }
     }
@@ -121,28 +121,25 @@ var queryFilterProto = {
 };
 
 describe('registerKind', function() {
-  it('should be able to register valid field metadata', function(done) {
+  it('should be able to register valid field metadata', function() {
     entity.registerKind('namespace', 'kind', blogPostMetadata);
-    done();
   });
 
-  it('should set the namespace to "" if zero value or null', function(done) {
+  it('should set the namespace to "" if zero value or null', function() {
     entity.registerKind(null, 'kind', blogPostMetadata);
     var meta = entity.getKind('', 'kind');
     assert.strictEqual(meta, blogPostMetadata);
-    done();
   });
 
-  it('should throw an exception if an invalid kind', function(done) {
+  it('should throw an exception if an invalid kind', function() {
     assert.throws(function() {
       entity.registerKind(null, '000', blogPostMetadata);
     }, /Kinds should match/);
-    done();
   });
 });
 
 describe('keyFromKeyProto', function() {
-   var proto = {
+  var proto = {
     partition_id: { namespace: '', dataset_id: 'datasetId' },
     path_element: [{ kind: 'Kind', name: 'Name' }]
   };
@@ -152,38 +149,46 @@ describe('keyFromKeyProto', function() {
     path_element: [{ kind: 'Kind', id: '111' }, { kind: 'Kind2', name: 'name' }]
   };
 
-  var protoHIncomplete = {
+  var protoIncomplete = {
+    partition_id: { namespace: 'Test', dataset_id: 'datasetId' },
+    path_element: [{ kind: 'Kind', id: '111' }, { kind: 'Kind2' }]
+  };
+
+  var protoInvalid = {
     partition_id: { namespace: 'Test', dataset_id: 'datasetId' },
     path_element: [{ kind: 'Kind' }, { kind: 'Kind2' }]
   };
 
-  it('should handle keys hierarchically', function(done) {
+  it('should handle keys hierarchically', function() {
     var key = entity.keyFromKeyProto(protoH);
     assert.deepEqual(key, new entity.Key({
-        namespace: 'Test',
-        path: [ 'Kind', 111, 'Kind2', 'name' ]
-      }));
-    done();
+      namespace: 'Test',
+      path: [ 'Kind', 111, 'Kind2', 'name' ]
+    }));
   });
 
-  it('should handle incomplete keys hierarchically', function(done) {
-    var key = entity.keyFromKeyProto(protoHIncomplete);
+  it('should not set namespace if default', function() {
+    var key = entity.keyFromKeyProto(proto);
+    assert.deepEqual(key, new entity.Key({ path: [ 'Kind', 'Name' ] }));
+  });
+
+  it('should not inject null into path if no id set', function(){
+    var key = entity.keyFromKeyProto(protoIncomplete);
     assert.deepEqual(key, new entity.Key({
-        namespace: 'Test',
-        path: [ 'Kind', null, 'Kind2', null ]
-      }));
-    done();
+      namespace: 'Test',
+      path: [ 'Kind', 111, 'Kind2' ]
+    }));
   });
 
-  it('should not set namespace if default', function(done) {
-    var key = entity.keyFromKeyProto(proto);
-    assert.deepEqual(key, new entity.Key({ path: [ 'Kind', 'Name' ] }));
-    done();
+  it('should throw if path is invalid', function() {
+    assert.throws(function() {
+      entity.keyFromKeyProto(protoInvalid);
+    }, /Invalid key. Ancestor keys require an id or name./);
   });
 });
 
 describe('keyToKeyProto', function() {
-  it('should handle hierarchical key definitions', function(done) {
+  it('should handle hierarchical key definitions', function() {
     var key = new entity.Key({ path: [ 'Kind1', 1, 'Kind2', 'name' ] });
     var proto = entity.keyToKeyProto(key);
     assert.strictEqual(proto.partition_id, undefined);
@@ -193,10 +198,9 @@ describe('keyToKeyProto', function() {
     assert.strictEqual(proto.path_element[1].kind, 'Kind2');
     assert.strictEqual(proto.path_element[1].id, undefined);
     assert.strictEqual(proto.path_element[1].name, 'name');
-    done();
   });
 
-  it('should detect the namespace of the hierarchical keys', function(done) {
+  it('should detect the namespace of the hierarchical keys', function() {
     var key = new entity.Key({
         namespace: 'Namespace',
         path: [ 'Kind1', 1, 'Kind2', 'name' ]
@@ -209,14 +213,13 @@ describe('keyToKeyProto', function() {
     assert.strictEqual(proto.path_element[1].kind, 'Kind2');
     assert.strictEqual(proto.path_element[1].id, undefined);
     assert.strictEqual(proto.path_element[1].name, 'name');
-    done();
   });
 
-  it('should handle incomplete keys with & without namespaces', function(done) {
-    var key = new entity.Key({ path: [ 'Kind1', null ] });
+  it('should handle incomplete keys with & without namespaces', function() {
+    var key = new entity.Key({ path: [ 'Kind1' ] });
     var keyWithNS = new entity.Key({
         namespace: 'Namespace',
-        path: [ 'Kind1', null ]
+        path: [ 'Kind1' ]
       });
 
     var proto = entity.keyToKeyProto(key);
@@ -231,43 +234,65 @@ describe('keyToKeyProto', function() {
     assert.strictEqual(protoWithNS.path_element[0].kind, 'Kind1');
     assert.strictEqual(protoWithNS.path_element[0].id, undefined);
     assert.strictEqual(protoWithNS.path_element[0].name, undefined);
-    done();
   });
 
-  it('should throw if key contains less than 2 items', function() {
+  it('should throw if key contains 0 items', function() {
     assert.throws(function() {
-      entity.keyToKeyProto(['Kind']);
+      var key = new entity.Key({ path: [] });
+      entity.keyToKeyProto(key);
+    }, /A key should contain at least a kind/);
+  });
+
+  it('should throw if key path contains null ids', function() {
+    assert.throws(function() {
+      var key = new entity.Key({
+        namespace: 'Namespace',
+        path: [ 'Kind1', null, 'Company' ]
+      });
+      entity.keyToKeyProto(key);
+    }, /Invalid key. Ancestor keys require an id or name./);
+  });
+
+  it('should not throw if last key path item is null', function() {
+    assert.doesNotThrow(function() {
+      var key = new entity.Key({
+        namespace: 'Namespace',
+        path: [ 'Kind1', 123, 'Company', null ]
+      });
+      entity.keyToKeyProto(key);
     });
   });
 });
 
 describe('isKeyComplete', function() {
-  it('should ret true if kind and an identifier have !0 vals', function(done) {
+  it('should ret true if kind and an identifier have !0 vals', function() {
     [
-      { key: new entity.Key({ path: [ 'Kind1', null ] }), expected: false },
-      { key: new entity.Key({ path: [ 'Kind1', 3 ] }), expected: true },
-      { key: new entity.Key({
-          namespace: 'Namespace',
-          path: [ 'Kind1', null ]
-        }), expected: false },
-      { key: new entity.Key({
-          namespace: 'Namespace',
-          path: [ 'Kind1', 'name' ]
-        }), expected: true }
+      {
+        key: new entity.Key({ path: [ 'Kind1' ] }),
+        expected: false
+      },
+      {
+        key: new entity.Key({ path: [ 'Kind1', 3 ] }),
+        expected: true
+      },
+      {
+        key: new entity.Key({ namespace: 'NS', path: [ 'Kind1' ] }),
+        expected: false
+      },
+      {
+        key: new entity.Key({ namespace: 'NS', path: [ 'Kind1', 'name' ] }),
+        expected: true
+      }
     ].forEach(function(test) {
       assert.strictEqual(entity.isKeyComplete(test.key), test.expected);
     });
-    done();
   });
 });
 
 describe('entityFromEntityProto', function() {
-  it('should support boolean, integer, double, string, entity and list values',
-      function(done) {
+  it('should support bool, int, double, str, entity & list values', function() {
     var obj = entity.entityFromEntityProto(entityProto);
-    assert.deepEqual(obj.linkedTo, new entity.Key({
-        path: [ 'Kind', 'another' ]
-      }));
+    assert.deepEqual(obj.linkedTo, new entity.Key({ path: [ 'Kind', '123' ]}));
     assert.strictEqual(obj.name, 'Some name');
     assert.strictEqual(obj.flagged, false);
     assert.strictEqual(obj.count, 5);
@@ -275,14 +300,11 @@ describe('entityFromEntityProto', function() {
     assert.strictEqual(obj.author.name, 'Burcu Dogan');
     assert.strictEqual(obj.list[0], 6);
     assert.strictEqual(obj.list[1], false);
-    done();
   });
 });
 
 describe('entityToEntityProto', function() {
-  it(
-      'should support boolean, integer, double, string, entity and list values',
-      function(done) {
+  it('should support bool, int, double, str, entity & list values', function() {
     var now = new Date();
     var proto = entity.entityToEntityProto({
       name: 'Burcu',
@@ -315,19 +337,16 @@ describe('entityToEntityProto', function() {
     var entityValue = properties[8].value.entity_value;
     assert.equal(entityValue.property[0].value.string_value, 'value1');
     assert.equal(entityValue.property[1].value.string_value, 'value2');
-    done();
   });
-
 });
 
 describe('queryToQueryProto', function() {
-  it('should support filters and ancestory filtering', function(done) {
+  it('should support filters and ancestory filtering', function() {
     var ds = datastore.dataset({ projectId: 'project-id' });
     var q = ds.createQuery('Kind1')
       .filter('name =', 'John')
       .hasAncestor(new entity.Key({ path: [ 'Kind2', 'somename' ] }));
     var proto = entity.queryToQueryProto(q);
     assert.deepEqual(proto, queryFilterProto);
-    done();
   });
 });