Testing Serializers in Ember - Part 2

Last reviewed on February 6, 2016

Not too long ago I wrote about Testing Serializers in Ember. The examples in the post went over testing the serialization process on responses and verifying methods like normalizeResponse and keyForRelationship worked correctly. But what if you want to verify that data is serialized correctly when it is sent to your API? Let me show you an approach that has worked well for me.

Let's say I have a cat model. When I create a new cat and call save(), imagine the API expects the request payload to look like this:

{
  "cats": [{ "name": "Frisky" }]
}

as opposed to this: (we are using the RESTSerializer but this process could work for the other serializers as well)

{
  "cat": {
    "name": "Frisky"
  }
}

For this scenario, the serialize and serializeIntoHash methods can be overridden to control the outgoing data format. The actual implementation of these methods isn't that important so you can skim over this if you'd like.

app/serializers/cat.js
export default DS.RESTSerializer.extend({
  serialize(snapshot) {
    return {
      cats: [{ name: snapshot.attr('name') }],
    };
  },
  serializeIntoHash(data, type, record, options) {
    delete data.cat;
    Ember.merge(data, this.serialize(record, options));
  },
});

The serialize method will control the format of the data when it is sent to the server, but without overriding serializeIntoHash, the data will look like this:

{
  "cat": {
    "cats": [{ "name": "Frisky" }]
  }
}

By default, the RESTSerializer will create a root key set as the model name. I don't want that root key in this scenario, so I am overriding serializeIntoHash to delete it.

Now let's look at how to test that the outgoing data is formatted correctly after overriding serialize and serializeIntoHash.

The Test

Whenever I am testing serializers, I use Pretender to mock out the backend. One of the great things about Pretender is that it keeps track of requests that were handled and those that were not in 2 public properties: handledRequests and unhandledRequests.

For this situation, we can use handledRequests to access the POST request that was made to /cats and inspect the request payload to verify that the data was formatted correctly.

First we need to set up Pretender and mock our endpoint:

moduleForModel('cat', 'Unit | Serializer | cat', {
  needs: ['serializer:cat'],
  beforeEach() {
    this.server = new Pretender(function () {
      this.post('/cats', function () {
        // default RESTSerializer response format
        // so that the promise from save() resolves
        let response = {
          cat: { id: 1, name: 'Frisky' },
        };

        return [200, {}, JSON.stringify(response)];
      });
    });
  },
  afterEach() {
    this.server.shutdown();
  },
});

Next, we can write our test:

test('save() sends the data formatted correctly', function (assert) {
  assert.expect(1);
  let store = this.store();

  Ember.run(() => {
    let cat = store.createRecord('cat', {
      name: 'Frisky',
    });

    cat.save().then(() => {
      let [request] = this.server.handledRequests;
      let requestPayload = JSON.parse(request.requestBody);
      assert.deepEqual(requestPayload, {
        cats: [{ name: 'Frisky' }],
      });
    });
  });
});

Here we are simply creating a cat record and calling save(). handledRequests is an array containing objects for each handled request. Because we are only dealing with 1 request here, we can access the first array element using array destructuring. Each handled request object has a property requestBody, which contains the request payload as a JSON string. With that, we can check to make sure that the data was sent across correctly.

Conclusion

I have to deal with a lot of APIs that don't follow the exact conventions of the JSONSerializer, RESTSerializer, or JSONAPISerializer so I find it necessary to test every serializer customization. This process has worked great for me so far. However, if your persistence layer isn't HTTP based, such as a local storage backend, this approach wouldn't work since you wouldn't be using Pretender. Have you tested a similar situation differently? If so, I would love to hear about it in the comments!


Pro Ember Data cover
Pro Ember Data available now!

Are you struggling with Ember Data?

In my new book Pro Ember Data, you will learn how to work with Ember Data efficiently, from APIs, adapters, and serializers to polymorphic relationships, using your existing JavaScript and Ember knowledge. This book will teach you how to adapt Ember Data to fit your custom API.

What You'll Learn

  • Review the differences between normalization and serialization
  • Understand how the built-in adapters and serializers in Ember Data
  • Understand how the built-in adapters and serializers in Ember Data work
  • Customize adapters and serializers to consume any API and write them from scratch
  • Handle API errors in Ember Data
  • Work with the Reddit API using Ember Data
  • Learn how to use polymorphic relationships
Check out Pro Ember Data on Amazon using my affiliate link
You can also find Pro Ember Data on Apress.

I've been enjoying @iamdtang's Pro Ember Data book. It's to the point and has lots of great examples. My favorite part was about error handling. This book is perfect after you've exhausted the official guides.

Ilya Radchenko

Product Developer at Applied Geographics

Ilya Radchenko

Pro Ember Data is such an approachable book @iamdtang! Good job. Loving the book so far! Thank you!

Lenora Porter

Software Engineer at Heroku

Lenora Porter

Great to see all those topics in which I struggled when I started working on Ember. Nice job, will check it out :)

AbulAsar S

Fullstack Developer

AbulAsar S

I haven't read David's new book, but his previous Ember Data book helped me a lot when I was starting out with Ember, so I am sure this book is a must if you're working with Ember Data!

Kenneth Larsen

Ember Learning Core Team member

Kenneth Larsen

I bought Ember Data in the Wild back in 2016 and found it really helpful. Looking forward to reading this.

@EmberLinks

Ember related news by Chris Masters

@EmberLinks