Guide to creating a unit test for canActivate guard in Angular routing

Seeking guidance on writing a unit test for angular routing with the canActivate guard. Encountering an error when using the guard on routes, but no error is thrown without it. Would appreciate a suitable example along with an explanation.

app-routing.module.ts

export const routes: Routes = [
 { path: '', component: LoginComponent },
 { path: 'home', canActivate: [AuthGuard], component: HomeComponent },
 { path: '**', redirectTo: '/home' }
]

auth-guard.guard.ts

export class AuthGuard implements CanActivate {
  private auth: IAuth;

  constructor(
    private storageService: StorageService,
    private router: Router
  ){}

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
      this.auth = JSON.parse(this.storageService.getLocalStorageItem('auth'));
      if(this.auth){
        return true;
      }

      this.router.navigate(['/'], {queryParams: {returnUrl: state.url}});
      return false;
  }
  
}

app-routing.module.test.ts

test(`Navigating to 'home' should redirect you to /home`, fakeAsync(() => {
        router.navigate(['/home']).then(() => {
            expect(location.path()).toEqual('/home');
        });
        flush();
    }));

Test Result

Testing Router › Navigating to 'home' should redirect you to /home

    Uncaught (in promise): Error: expect(received).toEqual(expected) // deep equality

    Expected: "/home"
    Received: "/"
    Error: expect(received).toEqual(expected) // deep equality

Answer №1

Make sure to set true for the canActivate method in AuthGuard before running the test. Here's an example using Jasmine:

describe('Test Description', () => {
    ...
    beforeEach(() => {
       const canActivateStub = () => ({ canActivate: () => true });
       ... all your imports
       TestBed.configureTestingModule({
           ... (all your imports)
           providers: [
               { provide: AuthGuard, useValue: canActivateStub}
           ]
       })
    })
    ...
})

To test the AuthGuard unit, follow these steps:

  describe('AuthGuard', () => {
        let service: AuthGuard;
    
    
        it('can load instance', () => {
            const routerStubInit = () => ({ navigate: (array, object) => ({}) });
            TestBed.configureTestingModule({
                providers: [
                    AuthGuard,
                    HttpClient,
                    HttpHandler,
                    { provide: Router, useFactory: routerStubInit },
                ]
            });
            service = TestBed.inject(AuthGuard);
    
            expect(service).toBeTruthy();
        });
    
        describe('canActivate', () => {
            it('makes expected calls', () => {
                const authenticationServiceStub = () => ({ userValue: {} });
                const routerStubInit = () => ({ navigate: (array, object) => ({}) });
                TestBed.configureTestingModule({
                    imports: [HttpClientTestingModule],
                    providers: [
                        AuthGuard,
                        HttpClient,
                        HttpHandler,
                        { provide: Router, useFactory: routerStubInit },
                        { provide: AuthenticationService, useFactory: authenticationServiceStub }
                    ]
                });
                service = TestBed.inject(AuthGuard);
    
                const activatedRouteSnapshotStub: ActivatedRouteSnapshot = <any>{};
                const routerStateSnapshotStub: RouterStateSnapshot = <any>{};
                spyOn(service, 'canActivate').and.callThrough();
                service.canActivate(activatedRouteSnapshotStub, routerStateSnapshotStub);
                expect(service.canActivate).toHaveBeenCalled();
            });
    
            it('makes expected calls', () => {
                const routerStubInit = () => ({ navigate: (array, object) => ({}) });
                const authenticationServiceStubNull = () => ({ userValue: null });
                TestBed.configureTestingModule({
                    imports: [HttpClientTestingModule],
                    providers: [
                        AuthGuard,
                        HttpClient,
                        HttpHandler,
                        { provide: Router, useFactory: routerStubInit },
                        { provide: AuthenticationService, useFactory: authenticationServiceStubNull }
                    ]
                });
                service = TestBed.inject(AuthGuard);
    
                const routerStub: Router = TestBed.inject(Router);
                const activatedRouteSnapshotStub: ActivatedRouteSnapshot = <any>{};
                const routerStateSnapshotStub: RouterStateSnapshot = <any>{};
                spyOn(routerStub, 'navigate').and.callThrough();
                service.canActivate(activatedRouteSnapshotStub, routerStateSnapshotStub);
                expect(routerStub.navigate).toHaveBeenCalled();
            });
        });
    });

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for customizing the appearance of a jQuery sortable target list prior to dropping items

When using jquery sortable, I am able to customize a placeholder inside a connected list specified in the connectWith option to visualize where the content will be dropped. However, I am struggling to find a way to style the actual list that contains the p ...

Setting up and customizing Express with Angular

Currently working on a straightforward Angular/Express todo-list app, I encountered some difficulties. As the project is still in progress, after running the code on localhost:3000, I noticed that {{ thing }} was displayed literally on the webpage. The di ...

Guide to creating an asynchronous function

Starting my first react website and needing assistance with writing an asynchronous JavaScript function. I am currently working on uploading user input files to firebase storage and then making a post request to the API in order to store the data in the da ...

How can we ensure only one click event is executed per element type in jQuery?

Here's the issue: I have a list of items with their own content organized in lists. Using jQuery, I've set it up so that when you click on an item, the list expands to show its content. However, when clicking on another item, the previously click ...

Unlocking the WiFi Security Key and Accessing Connected Devices with Javascript

When utilizing the command line netsh wlan show interfaces, it displays various information. However, I am specifically interested in extracting the data Profile : 3MobileWiFi-3D71. My goal is to retrieve only the content after the : so that ...

How can you leverage the power of useState in a custom React hook?

Here is a code snippet for a custom hook I created: const useShowBg = () => { const [showBg, useShowBg] = useState(false); return [showBg, useShowBg]; }; export default useShowBg; In my component, I import and use the custom hook like this: im ...

Divide the sentence using unique symbols to break it into individual words, while also maintaining

Is there a way to split a sentence with special characters into words while keeping the spaces? For example: "la sílaba tónica es la penúltima".split(...regex...) to: ["la ", "sílaba ", "tónica ", "es ", "la ", "penúltima"] ↑ ...

Combining Javascript and Django for a powerful web development solution

Having trouble setting up JS on my Django web app, despite reading through the documentation and previous queries. Using the Django dev server with the following file structure: mysite/ __init__.py MySiteDB manage.py settings.py ...

Material-ui does not adjust Typography color based on the theme selected

Exploring material-ui, I have implemented two themes: const darkTheme = createMuiTheme({ palette: { type: "dark" } }); const lightTheme = createMuiTheme({ palette: { type: "light" } }); However, when utilizing the Typography component, t ...

Protector of the young travelers' paths

I have encountered a recurring issue with implementing Guards on my pages. Despite referencing multiple solutions from StackOverflow, none of them seem to work for me. Take this example for instance. This is my first attempt at restricting access to cert ...

Attempting to eliminate the readonly attribute from HTML using Python

Seeking assistance with removing the readonly tag from an input field using Python and Selenium. Can anyone lend a hand? Datepicker Image: HTML: <input id="startDate" name="START_DATE" type="text" class="date hasDate ...

What steps should I take in order to correctly implement the onChange event and retrieve the event.target.value in my

Incorporating useForm, yupResolver, and Yup for validation caused issues with the previously functioning handleChange function. The value variable doesn't log anything when console.logged, indicating a disconnect with the input field content. Addition ...

What is the purpose of exporting both a class and a namespace under the same name?

While exploring some code, I came across a situation where a class and a namespace with identical names were exported from a module. It seems like the person who wrote this code knew what they were doing, but it raised some questions for me. Could you shed ...

Trigger the function when the keyboard event is deactivated

Is there a way to continuously run the top set interval whenever I lift my finger from the space key? When I try using the key up event, it only executes that function once. I'm not sure how to implement if/else logic when adding an event listener. se ...

Steps for preventing text manipulation in ng2-ace-editorWould you like to restrict users from copying, pasting

How can I prevent users from copying, pasting, and dropping text in ng2-ace-editor? https://github.com/fxmontigny/ng2-ace-editor is the library I implemented in my Angular 5 application. ...

What steps can I take to prompt a ZMQ Router to throw an error when it is occupied?

In my current setup, I have a configuration with REQ -> ROUTER -> [DEALER, DEALER... DEALER]. The REQ acts as a client, the ROUTER serves as a queue, and the DEALER sockets are workers processing data and sending it back to ROUTER for transmission to ...

Angular components are experiencing issues with implementing Tailwind CSS

After incorporating Tailwind CSS into my Angular project, I noticed that it functions successfully at the root level of the project. However, when it comes to the SCSS files within individual components, it seems to be ineffective. Do I need to manually ...

An error message pops up when using Next.js with Sass, indicating that a suitable loader is required to handle this file type

I've been struggling to properly wire up my next.js project with SCSS, but no matter what I try, it just won't work. I double-checked my setup for compiling SCSS files, but the error message keeps popping up: /scss/style.scss 1:0 Module parse f ...

Specify a preset selection time in a TextField of time type in Material UI

I am looking to establish a default time for a textfield of type time in Material UI. My requirement is that before the user clicks on the picker, no time should be pre-set, but once they click, 08:00 should appear as the default time to choose from. View ...

The functionality of a JQuery post within a PHP loop is not functioning optimally

I am encountering an issue with my list.php file, which generates a list with items and a button (Erledigt!) that triggers a jQuery Post to wishdelete2.php. The problem is that the jQuery post sometimes requires multiple clicks (3x or 5x) before it process ...