Maintain business logic and display separate logic
This applies only to the DOM. Many APIs, both in the browser and in Node, are designed to trigger and listen to events or wait for other types of asynchronous operation to complete. Rule of thumb: If you write a lot of anonymous callback functions, your code may not be easy to test.
// hard to test $('button').on('click', () => { $.getJSON('/path/to/data') .then(data => { $('#my-list').html('results: ' + data.join(', ')); }); }); // testable; we can directly run fetchThings to see if it // makes an AJAX request without having to trigger DOM // events, and we can run showThings directly to see that it // displays data in the DOM without doing an AJAX request $('button').on('click', () => fetchThings(showThings)); function fetchThings(callback) { $.getJSON('/path/to/data').then(callback); } function showThings(data) { $('#my-list').html('results: ' + data.join(', ')); }
Use asynchronous callbacks or Promises
The most common way to solve this problem is to pass a callback function as a parameter to a function that runs asynchronously. In your unit tests, you can fulfill your statements in the callback you pass.
// hard to test; we don't know how long the AJAX request will run function fetchData() { $.ajax({ url: '/path/to/data' }); } // testable; we can pass a callback and run assertions inside it function fetchDataWithCallback(callback) { $.ajax({ url: '/path/to/data', success: callback, }); } // also testable; we can run assertions when the returned Promise resolves function fetchDataWithPromise() { return $.ajax({ url: '/path/to/data' }); }
Here are the details
source share