javascriptjasmine

Writing tests in Jasmine for functions with dependancy on data from remote host whithout making actual fetch request


I apologize beforehand for this post, but I am struggling to figure out how to mock data from fetch request without making request itself and then test functions which have this data as dependency in Jasmine.

Here is my JavaScript code:

let products = []; //Array where transformed data from fetch request is stored for further use

function getProduct(productId) {//function to get product with certain id

  let matchProduct = products.find(product => product.id === productId)

  return matchProduct;

}


class Product { /*Example of classes used in function below, I can test them fine and they pass tests*/

  id;
  image;
  name;
  rating;
  priceCents;
  keywords;

  constructor(productDetails) {

    this.id = productDetails.id;
    this.image = productDetails.image;
    this.name = productDetails.name;
    this.rating = productDetails.rating;
    this.priceCents = productDetails.priceCents;
    this.keywords = productDetails.keywords;

  }

  getStars() {
    return `images/ratings/rating-${this.rating.stars * 10}.png`;
  }

  getPrice() {
    return `$${formatCurrency(this.priceCents)}`;
  }

  extraInfoHTML() {
    return ''
  }
}



function loadProducts() {

  const promise = fetch('link to host').then(response => {
    
    return response.json(); //host responds with array of objects if translated in json

  }).then(productData => { /*manipulating received array of objects to transform objects into classes and saving in new array*/

    products = productData.map(productDetails => {

      if (productDetails.keywords.includes('appliances')) {

        productDetails.type = 'appliance';
        productDetails.instructionLink = 'images/appliance-instructions.png';
        productDetails.warrantyLink = 'images/appliance-warranty.png';

        return new Appliance(productDetails);

      }

      if (productDetails.type === 'clothing') {

        return new Clothing(productDetails);

      }

      return new Product(productDetails);

    });

  }).catch(error => {

    console.error(error.message);

  });

  return promise;
}

All code works well and does what it does outside of test environment, but I got interested in how to test function getProduct() without making actual fetch request to remote host and how to test if remote host responded with correct data on request itself and/or request used correct link using Jasmine.


Solution

  • You should mock the fetch request and test your function in isolation. Here is my full check list of properly testing a function in isolation using Jasmine:

    Here are sample tests to illustrate the above:

    
    // Import your module that contains products, getProduct, loadProducts
    // const { products, getProduct, loadProducts, Product, Appliance } = require('./your-products-file');
    
    describe('Product Functions', () => {
      let originalFetch;
      
      beforeEach(() => {
        // Reset products array
        products = [];
        
        // Store original fetch
        originalFetch = global.fetch;
      });
      
      afterEach(() => {
        // Restore original fetch
        global.fetch = originalFetch;
      });
    
      describe('getProduct', () => {
        it('should return product with matching id', () => {
          // Mock products data
          products = [
            new Product({ id: '1', name: 'Test Product', rating: { stars: 4 }, priceCents: 1000 }),
            new Product({ id: '2', name: 'Another Product', rating: { stars: 5 }, priceCents: 2000 })
          ];
          
          const result = getProduct('1');
          
          expect(result.id).toBe('1');
          expect(result.name).toBe('Test Product');
        });
        
        it('should return undefined for non-existent product', () => {
          products = [new Product({ id: '1', name: 'Test', rating: { stars: 4 }, priceCents: 1000 })];
          
          const result = getProduct('999');
          
          expect(result).toBeUndefined();
        });
      });
    
      describe('loadProducts', () => {
        it('should fetch and transform products correctly', async () => {
          const mockData = [
            { id: '1', name: 'Regular Product', rating: { stars: 4 }, priceCents: 1000, keywords: [] },
            { id: '2', name: 'Appliance', rating: { stars: 5 }, priceCents: 2000, keywords: ['appliances'] }
          ];
          
          // Mock fetch
          global.fetch = jasmine.createSpy('fetch').and.returnValue(
            Promise.resolve({
              json: () => Promise.resolve(mockData)
            })
          );
          
          await loadProducts();
          
          expect(fetch).toHaveBeenCalledWith('link to host');
          expect(products.length).toBe(2);
          expect(products[0]).toBeInstanceOf(Product);
          expect(products[1]).toBeInstanceOf(Appliance);
        });
        
        it('should handle fetch errors', async () => {
          spyOn(console, 'error');
          
          global.fetch = jasmine.createSpy('fetch').and.returnValue(
            Promise.reject(new Error('Network error'))
          );
          
          await loadProducts();
          
          expect(console.error).toHaveBeenCalledWith('Network error');
        });
      });
    });