0

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.

3
  • What interpreters are you targeting? Commented Aug 29 at 14:39
  • @EricMORAND My apologies, but I afraid I don't understand the question. Code written in plain Javascript and tests I use Jasmine framework(test files in separate folder). Commented Aug 30 at 9:48
  • 1
    @EricMORAND Browser oriented project,not nodejs if you asking about this. Commented Aug 30 at 9:57

1 Answer 1

0

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:

  • Mock the global fetch: Replace global.fetch with a Jasmine spy that returns a Promise

  • Test without real requests: Use jasmine.createSpy() to create a fake fetch function

  • Verify test calls: Check that fetch was called with the correct URL

  • Mock response data: Return sample test data instead of the real API response data

  • Test error handling: Mock rejected promises to test error scenarios

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'); }); }); }); 
Sign up to request clarification or add additional context in comments.

1 Comment

Conceptually this is correct. But I think advising to mock a global method is not doing anybody favours. A test is - by nature - run against a host that matches the interface of the targeted host. If the business logic needs a method from this host, it must get it from the host instance that has been passed at construct time or at call time. Using global functions violate most of the SOLID principles.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.