2

I'm making unit tests on my rest api with mocha and chai. For the moment, for each request, (let's say a POST for example) I test the whole response body (excluding non static data like ids etc...). The problem is : if some day I decide to change the model of my resource, like maybe adding a new field. The actual tests won't check this new field. So I'll have to update every test related to this resource, which can be complicated as the number of tests increases.

So my question is : Am I doing it the right way ? If not, what should I test in my api responses and what should I not ?

nicholaswmin
  • 21,686
  • 15
  • 91
  • 167
FantzFoXx
  • 53
  • 5
  • You are not doing unit test, you are doing end to end testing. – Ratul Sharker Nov 28 '19 at 14:02
  • Oh yes, thank's. Sorry I'm a bit new with testing. – FantzFoXx Nov 28 '19 at 14:16
  • It sounds that you aren't exchanging data based on standardized media types but on some proprietary types. Note that REST shouldn't have [typed resources meaningful to clients](http://soabits.blogspot.com/2012/04/restful-resources-are-not-typed.html) but instead rely on content-type negotiation of standardized media-types. This is pretty similar to the Web. Here you also don't get i.e. respective car manufacturer details in a proprietary format but in a HTML structure that a browser knows how to render or a scraper how to parse. – Roman Vottner Nov 28 '19 at 15:40

1 Answers1

2

The DRY (Don't repeat yourself) principle applies to testing as well, but don't overdo it. Tests should be "DAMP not DRY".

...if some day I decide to change the model of my resource, like maybe adding a new field. The actual tests won't check this new field. So I'll have to update every test related to this resource...

In this case what I usually do is create a Chai custom assertion that defines a set of assertions for a specific type, e.g a Todo.

This custom assertion is then (re)used in all relevant tests to verify that the returned object(s) actually pass the assertions for Todo, without having to repeat the same assertions in each and every test.

Here's an example:

const chai = require('chai')
const chaiHttp = require('chai-http')
const server = 'https://jsonplaceholder.typicode.com'

chai.should()
chai.use(chaiHttp)
// Define a custom assertion for 'Todo'. We expect that a Todo always 
// has an `id` property of type `Number` and a `title` property of
// type `String`.
chai.use((chai, utils) => {
  utils.addProperty(chai.Assertion.prototype, 'Todo', function () {
    this._obj.should.have.property('id')
    this._obj.should.have.property('title')

    this._obj.id.should.be.a('Number')
    this._obj.title.should.be.a('String')
  })
})

// Begin Tests

describe('Retrieve a Todo', () => {
  it('returns a single Todo by ID', () => {
    return chai.request(server)
      .get('/todos/1')
      .then(res => {
        res.should.have.status(200)
        // Use the custom assertion to check if returned object
        // passes the assertions for `Todo`.
        res.body.should.be.a.Todo
      })
  })
})

describe('Retrieve all Todos', () => {
  it('returns a list containing 200 Todos', () => {
    return chai.request(server)
      .get('/todos')
      .then(res => {
        res.should.have.status(200)
        res.body.should.be.an('Array')
        res.body.should.have.length(200)
        // Reuse the custom assertion to check if all returned objects
        // pass the assertions for `Todo`.
        res.body.forEach(todo => todo.should.be.a.Todo)
      })
  })
})

If in the future I add a new field on Todo, i.e completed, all I need to do is modify the custom assertion like so:

chai.use((chai, utils) => {
  utils.addProperty(chai.Assertion.prototype, 'Todo', function () {
    this._obj.should.have.property('id')
    this._obj.should.have.property('title')
    this._obj.should.have.property('completed')

    this._obj.id.should.be.a('Number')
    this._obj.title.should.be.a('String')
    this._obj.completed.should.be.a('Boolean')
  })
})

... what should I test in my api responses and what should I not ?

As a minimum I would check if:

  • The response HTTP status is correct.
  • The response body has the appropriate properties and correct types for each property.
  • If the response is a list of items, I would check if the response body is indeed an Array, if it has the length I expect it to have and if each element in the Array has the correct properties and types.

There's no "rules" here. At the end it's a risk/time decision. Maintaining a test suite takes time. If I'm building a simple todo app for my own use, I won't concern myself too much with exhaustive testing. However if I'm building a public payment server, I'd definitely want my tests to be as exhaustive as possible.

nicholaswmin
  • 21,686
  • 15
  • 91
  • 167
  • I should have read the documentation a little more, i guess. Thank you very much for your explanations, and the links ! – FantzFoXx Dec 02 '19 at 13:52