JavaScript Object.defineProperty for a function: create mock object instances in Jest or AVA
Tags: javascript, testing, jest, webdev
Published: 2019-04-09 22:09:49

---
title: JavaScript Object.defineProperty for a function: create mock object instances in Jest or AVA
published: true
tags: javascript, testing, jest, webdev
canonical_url: https://codewithhugo.com/js-object-defineproperty-for-function/
---

This post goes through how to use `Object.defineProperty` to mock how constructors create methods, ie. non-enumerable properties that are functions.

The gist of `Object.defineProperty` use with a function value boils down to:

```js
const obj = {}

Object.defineProperty(obj, 'yes', { value: () => Math.random() > .5 })

console.log(obj) // {}
console.log(obj.yes()) // false or true depending on the call :D
```

As you can see, the `yes` property is not enumerated, but it does exist. That's great for setting functions as method mocks.

It’s useful to testing code that uses things like Mongo’s `ObjectId`. We don’t want actual ObjectIds strewn around our code. Although I did create an app that allows you generate ObjectId compatible values ([see it here Mongo ObjectId Generator](https://observablehq.com/@hugodf/mongodb-objectid-generator)).

All the test and a quick explanation of what we’re doing and why we’re doing it, culminating in our glorious use of `Object.defineProperty`, is on GitHub [github.com/HugoDF/mock-mongo-object-id](https://github.com/HugoDF/mock-mongo-object-id). Leave it a star if you’re a fan 🙂 .


## Testing Mongo ObjectId

You use them in your persistence layer, you usually want to convert a string to an ObjectId using the `ObjectId()` constructor

See the following snippet:

```js
const { ObjectID, MongoClient } = require('mongodb')
const mongoClient = new MongoClient()
async function getUserIdFromSession(sessionId) {
  const session = await (await mongoClient.connect()).collection('sessions').findOne({
    _id: ObjectId(sessionId)
  });
  
  return session.userId && session.userId.toString();
}
```


### A naive mock

An naive mock implementation would be:

```js
const mockObjectId = data => data;
```

We’re relying on the fact that the `.toString`  method exists on strings:

```js
'myString'.toString() // 'myString'
```

The issue is that it’s not an object, so it behaves differently


### A better mock

So those are our 3 requirements:

- `toString()` should exist and return whatever’s passed into the constructor
- it should be an object
- `ObjectId('a')` should deep equal `ObjectId('a')`

```js
const test = require('ava')
const mockObjectId = data => {
  return {
    name: data,
    toString: () => data
  };
}

test("toString() returns right value", t => {
  t.is(mockObjectId("foo").toString(), "foo");
});
test("it's an object", t => {
  const actual = mockObjectId("foo");
  t.is(typeof actual, "object");
});
test.failing("two objectIds with same value are equal", t => {
  const first = mockObjectId("foo");
  const second = naiveObjectId("foo");
  t.deepEqual(first, second);
});
```

Failure:

```sh
Difference:

  {
    name: 'foo',
-   toString: Function toString {},
+   toString: Function toString {},
  }
```


`toString` is a new function for each mock instance… which means they’re not deep equal.


### The right mock

```js
const test = require("ava");

const mockObjectId = data => {
  const oid = {
    name: data
  };
  Object.defineProperty(oid, "toString", {
    value: () => data
  });
  return oid;
};

test("toString() returns right value", t => {
  t.is(mockObjectId("foo").toString(), "foo");
});
test("it's an object", t => {
  const actual = mockObjectId("foo");
  t.is(typeof actual, "object");
});
test("two objectIds with same value are equal", t => {
  const first = mockObjectId("foo");
  const second = mockObjectId("foo");
  t.deepEqual(first, second);
});
```
## How Object.defineProperty saved our bacon

It’s about enumerability. We want to mock an Object, with methods on it, without the methods appearing when people enumerate through it.

`Object.defineProperty` allows you to set whether or not the property is enumerable, writable, and configurable as well as a value or a get/set (getter/setter) pair (see [MDN Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)).

There are 2 required descriptor (configuration) values: `configurable` (if true, the property can be modified or deleted, false by default), `enumerable` (if true, it will show during enumeration of the properties of the object, false by default).

There are also some optional descriptor (configuration) values: `value` (value associated with property, any JS type which includes function), `writable` (can this property be written to using the assignment operator, false by default), `get` and `set` (which are called to get and set the property).

This is an example call:

```js
const o = {}
Object.defineProperty(
  o,
  'me',
  {
    value: 'Hugo',
    writable: false, // default: false
  }
)

console.log(o.me) // 'Hugo'
```


All the code is up at [github.com/HugoDF/mock-mongo-object-id](https://github.com/HugoDF/mock-mongo-object-id). Leave it a star if you’re a fan 🙂.

[<svg xmlns="http://www.w3.org/2000/svg" style="height:12px;width:auto;position:relative;vertical-align:middle;top:-2px;fill:white" viewbox="0 0 32 32"><title>unsplash-logo</title>
<path d="M10 9V0h12v9H10zm12 5h10v18H0V14h10v9h12v-9z"></path></svg>rawpixel](https://unsplash.com/@rawpixel?utm_medium=referral&utm_campaign=photographer-credit&utm_content=creditBadge "Download free do whatever you want high-resolution photos from rawpixel")