Programmatically check if lazy-loading works

Viewed 52

In Angular, it is quite easy to break lazy-loading. For example, all it takes is for someone to carelessly import something from a lazy-loaded module into the app module and this module is eagerly loaded. Therefore, I usually check for such errors when reviewing PRs. Currently, I do this manually by looking at the logs of ng build or by inspecting the network logs in the browser DevTools. However, I would like to automate this repetitive task in our CI pipeline.

After trying various methods, I can only think of two more or less suboptimal approaches:

  1. A script that builds the app and then checks if the expected number of lazy-loaded chunks is in the dist folder. However, this does not test when a chunk is loaded.
  2. E2e tests that assert that a specific .js chunk file is loaded when the browser navigates to a specific route.

Is there any better way to programmatically check if lazy-loading works for all lazy-loaded modules?

EDIT: To be more precise: there are some answers on StackOverflow regarding this topic, but they usually rely so heavily on stubbing and mocking that, in the end, they don't test the actual implementation.

2 Answers

Maybe this is what you meant in option 2 - I would set up an intercept to verify the chunk is loaded after an action that invokes the lazy load.

cy.intercept('**/*.chunk.js', { times: 1 })       // one time only
  .as('chunk'); 
cy.visit('/orders');      
cy.wait('@chunk');                                // fails if not lazy loaded

I'm not sure if that minimatch pattern will catch the chunk, but you can also check using javascript.

cy.intercept('*', { times: 1 }, (req) => {
  if (req.url.includes('chunk')) {
    req.alias = 'chunk'
  }
});

Since this question is now active without answers for over a month, I will share how I was finally able to solve it. May it help someone in the future.

The solution was the following:

  1. In the angular.json set the build option namedChunks to true. This causes the lazy chunks to be named after their file path.
  2. For every lazy-loaded module, write an e2e test like so:
    // Assume our lazy-loaded chunk file is called `our_lazy_feature_module.js`
    
    
    it('should lazy-load /our-lazy-feature', () => {
    
      // set up the interceptor
      cy.intercept({ method: 'GET', url: /^.*our_lazy_feature.*$/g })
        .as('chunkFileRequests')
    
      // visit about page and assert that the chunk file has not yet been loaded
      cy.visit('/'); // navigate to a eagerly loaded route
      cy.get('@chunkFileRequests.all')
        .should('have.length', 0);
    
      // click on the link to the lazy-loaded page and assert that the chunk file gets loaded
      cy.contains('a', 'Link to our lazy feature')
        .click();
      cy.contains('h1', 'Welcome to our lazy feature')
        .should('be.visible');  // wait for the navigation to complete
      cy.get('@chunkFileRequests.all').should('have.length', 1);
    
    });
    
Related