1

Let's say I have the following method:

function sendUserDataToServer(userData) {
    // Do some configuration stuff
    $.ajax('SomeEndpoint', userData);
}

When writing a unit test for this function, I could go one of two ways:

  1. Create a spy around $.ajax and check that it was called with the expected parameters. An actual XHR request will not be sent.
  2. Intercept the ajax request with a library like SinonJs and check the XHR object to make sure it was configured correctly.

Why I might go with option 1: It separates the functionality of $.ajax from my code. If $.ajax breaks due to a bug in jQuery, it won't create a false-negative in my unit test results.

Why I might go with option 2: If I decide I want to use another library besides jQuery to send XHRs, I won't have to change my unit tests to check for a different method. However, if there's a bug in that library, my unit tests will fail and I won't necessarily know it's the library and not my actual code right away.

Which approach is correct?

Andrew Whitaker
  • 124,656
  • 32
  • 289
  • 307
Levi Hackwith
  • 9,232
  • 18
  • 64
  • 115

4 Answers4

4

Your first option is correct. The purpose of your unit test is twofold

  • to verify your code contract
  • to verify that it communicates with other things it uses.

Your question is about the interaction with $.ajax, so the purpose of this test is to confirm it can talk correctly to its collaborators.

If you go with option 1, this is exactly what you are doing. If you go with option 2, you're testing the third party library and not that you're executing the communication protocol correctly.

You should make both tests though. The second test is probably fast enough to live with your unit tests, even though some people might call it an integration test. This test tells you that you're using the library correctly to create the object you intended to create. having both will let you differentiate between a bug in your code and the library, which will come in handy if there is a library bug that causes something else in your system to fail.

munk
  • 12,340
  • 8
  • 51
  • 71
1

My opinion: option 1 is more correct within unit tests.

A unit test should test the unit of code only. In your case this would be the JavaScript under your control (e.g. logic within the function, within a class or namespace depending on the context of your function).

Your unit tests should mock the jQuery call and trust that it works correctly. Doing real AJAX calls is fine, but these would be part of integration tests.

See this answer regarding what is and what isn't a unit test.

As an aside, a third way would be to do the AJAX calls via a custom proxy class which can be mocked in tests but also allows you to switch the library you use.

Community
  • 1
  • 1
Oli Wennell
  • 846
  • 1
  • 7
  • 10
1

I would use option 1 but go with the one that makes the tests easiest to understand.

You are depending on jQuery in your code, so you don't need to worry about it actually working as you point out. I have found that it makes the tests cleaner as you specify your parameters and the response and have your code process it. You can also specify which callback should be used (success or error) easily.

As for changing the library from jQuery. If you change the code your test should fail. You could argue that your behavior is now different. Yes, you are making an Ajax call but with a different dependency. And your test should be updated to reflect this change.

Both approaches are really correct. If you are creating your own wrapper for sending Ajax calls, it might make sense to intercept the XHR request or manually creating the request and sending it yourself. Choose the option that makes your test easiest to understand and maintain.

Schleis
  • 41,516
  • 7
  • 68
  • 87
1

the most important thing is to test your own code. separate business logic from the IO code and your business logic. after that you should have methods without any logic that do the actual IO (exactly as in your example). and now: should you test that method? it depends. tests are to make you feel safer.

  • if you really worry that jQuery can fail and you can't afford such unlikely scenario than test it. but not with some mocking library but do full end-to-end integration tests
  • if you're not sure if you are using jQuery correctly then do the 'learning tests'. pass a parameters to it and check what kind of request it produces. mocking tests should be enough in this situation
  • if you need a documentation of a contract between frontend and backend then you may choose if you prefer to test if backend meets the contract, frontend or both
  • but if none of above applies to you then isn't it a waste of time to test external, well established framework? jquery guys for sure have tested it. still you will probably need some kind of integration tests (automatic or manual) to check if your frontend interacts with backend properly. and those tests will cover the network layer
piotrek
  • 13,982
  • 13
  • 79
  • 165