Tips for unit testing the getCurrentNavigation().extras.state method in Angular 7

Attempting to create a unit test for getCurrentNavigation().extras.state with jasmine.

To address the problem, the router method was spied on.

The component's file,

@Component({
  selector: 'app-location-list',
  templateUrl: './location-list.component.html',
  styleUrls: ['./location-list.component.scss']
})
export class LocationListComponent implements OnInit{

  locationId;
  locationName;
  constructor(private activatedRoute: ActivatedRoute, private router: Router) {
    if (this.router.getCurrentNavigation().extras.state) {
      this.locationId = this.router.getCurrentNavigation().extras.state.locationId;
    }
    if (this.router.getCurrentNavigation().extras.state) {
      this.locationName = this.router.getCurrentNavigation().extras.state.locationName;
    }
  }
  ngOnInit() { }
}

The Spec file,

describe('LocationListComponent ', () => {
  let component: LocationListComponent ;
  let fixture: ComponentFixture<LocationListComponent >;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule
      ],
      declarations: [
        LocationListComponent 
      ],
      providers: []
    })
      .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LocationListComponent );
    component = fixture.componentInstance;
    spyOn(Router.prototype, 'getCurrentNavigation').and.returnValues({ 'extras': { 'state': { 'locationId': 100, 'locationName': "UK" } } });
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

Encountering the error message below:

TypeError: Cannot read property `extras` of null

In need of assistance to fix this issue. Developed using Angular 7.2

Answer №1

When attempting to utilize both RouterTestingModule and

{provide: Router, useClass: RouterStub}
, an error may be thrown stating
cannot read property 'root' of undefined

To avoid this issue, one solution is to directly create a spy for Route and return its value

describe('LocationListComponent', () => {
  let component: LocationListComponent ;
  let fixture: ComponentFixture<LocationListComponent>;
  let router: jasmine.SpyObj<Router>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule
      ],
      declarations: [
        LocationListComponent 
      ],
    })
      .compileComponents();
  }));

  beforeEach(() => {
    router = TestBed.get(Router);
    spyOn(router, 'getCurrentNavigation').and.returnValue({ extras: { state: { message: 'msg'} } } as any);
    fixture = TestBed.createComponent(LocationListComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

Answer №2

To simplify this process, you can utilize the stub and useClass methods which can be easily applied to other spec files by creating them in a separate file and exporting the RouterStub class:

In your spec file, define a stub that mimics the methods of the Router class:

class RouterStub{
 getCurrentNavigation(){
   return {
      extras: {
         state:{
           locationId: 'someId',
           locationName: 'someName'
         }
       }
     }
   }
}

Then, within the beforeEach() block:

describe('LocationListComponent ', () => {
  let component: LocationListComponent ;
  let fixture: ComponentFixture<LocationListComponent >;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule
      ],
      declarations: [
        LocationListComponent 
      ],
      providers: [ {provide: Router, useClass: RouterStub}]
    })
      .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LocationListComponent );
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

Answer №3

To effectively test the functionality of router.getCurrentNavigation(), follow these 3 simple steps:

  1. First, create a mock router object:

    const fakeRouter = {
      getCurrentNavigation: jasmine.createSpy('getCurrentNavigation')
    };
    
  2. Next, inject this mock object into the providers array:

    providers: [{ provide: Router, useValue: fakeRouter }]
    
  3. Finally, define the desired mocked return value within the beforeEach() function:

    fakeRouter.getCurrentNavigation.and.returnValue({
      extras: {
        state: {
          example: ''
        }
      }
    });
    

Answer №4

Make sure to retrieve the router instance from the fixture

routerInstance = fixture.debugElement.injector.get(Router);

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

Decoding the Mystery: What Do the + and # Symbols Mean in Angular 4 Routes?

Check out the repository at https://github.com/AngularClass/angular-starter https://i.sstatic.net/ITi80.png I noticed the use of + and # for referencing within loadChildren and naming conventions in the folder... After consulting the Angular documentati ...

Using a tuple as a key in a Map in Typescript/Javascript can be a powerful

Encountered a strange bug in my code where I'm struggling to achieve constant time lookup from a Map when using a tuple as the key. Here is an example highlighting the issue, along with the workaround I am currently using to make it function: hello. ...

The MatFormField component requires a MatFormFieldControl to be present in order to function properly. This error may occur if the

HTML CODE `<mat-form-field> <input type="file" (change)="onFileSelected($event)" name="categoryImage" > </mat-form-field>` TS code onFileSelected(event){ console.log(event);} Encountering an error even before beginning the ope ...

Is there a way to execute a code snippet just once when focusing on a specific field?

<form id="myForm"> <label for="fname">First name:</label><br> <input type="text" id="fname" name="fname"><br> <label for="mname">Middle name:</label> ...

Angular allows for the dynamic inclusion and exclusion of components, providing a flexible

In my setup, I have a container that houses two distinct components. The first component receives a list of users from the backend. Upon clicking on a specific user, I aim to display all of their detailed information in the main container of the second co ...

Creating a JSON file using an object to send requests in Angular

In my Angular 7 project, I am trying to send a multipart request to the server that includes a file (document_example.pdf) and a json file containing a data object (data_object_example.json). The content of data_object_example.json is as follows: { " ...

Getting the highest level object in a nested Angular component structure

Currently, I am in the process of developing a query builder using Angular that bears resemblance to the JQuery builder. However, I have encountered an issue related to emitting the data. Whenever I click on the addGroup or addRule buttons, a new group is ...

Using Angular 5 to integrate a jQuery plugin

Recently, I've started learning Angular and am currently using version 5. I need to integrate a plugin called 'jquery-circle-progress' into my project. The plugin can be found at this link: I managed to install the plugin using npm and adde ...

What is the best way to retrieve a Map object from Firebase in a TypeScript environment?

Currently, I am working on a cloud function in TypeScript, where I am attempting to retrieve a Map object (also known as nested objects or maps) from Firebase in order to iterate through it. Here is the structure of my Firebase data: https://i.sstatic.ne ...

Discovering the art of incorporating an API post response into an array with the assistance of the pipe and map operator within the Angular framework. Witness the transformative power as

Currently, I am involved in a project where I need to interact with a post API in my component.ts file. The API response is structured as follows: email: string, employeeAddress: string, employeeId: string, employeeLevel: string I'm uncertain ...

How to authenticate users using JWT in Angular and retrieve user data?

I've developed an Angular project with JWT login functionality to communicate with the backend. Once I receive a token, my goal is to retrieve user information from the backend. Below is the code snippet from my authentication service: login(username: ...

Is there a convenient method to combine arrays of objects in JavaScript using ellipses or an equivalent approach?

let array = [ {id: 1, data: {foo: "bar 1"}}, {id: 2, data: {foo: "bar 2"}} ]; //If ID doesn't exist, add new element to the array array = [...array, {id: 3, data: {foo: "bar 3"}}] console.log(array); //If ID exists, replace data object with new ...

Error: Attempting to access a property called 'sign' on an undefined value

I encountered an issue while signing my transaction where I received an error message stating sendTransaction needs signer. Even though both message (encrypted using keccak256) and signer have values, I am unsure why there is a problem when executing the w ...

Utilize the power of TypeScript to display data with impact

Recently, I have been working on a project in TypeScript where I need to fetch data from a URL and display it in my component. Transitioning from JavaScript to TypeScript has been a bit challenging for me. Here is the code snippet I have been working on: i ...

In the realm of JavaScript and TypeScript, the task at hand is to locate '*' , '**' and '`' within a string and substitute them with <strong></strong> and <code></code>

As part of our string processing task, we are looking to apply formatting to text enclosed within '*' and '**' with <strong></strong>, and text surrounded by backticks with <code> </code>. I've implemented a ...

Can we define the input and return types for functions using httpsCallables?

I have implemented a callable function in my app to update user claims. The functions are written in Typescript and I have used an interface to define the required data shape for the function. My objective is to make it easier for any developer on the tea ...

Issue with retrieving all phone numbers from a contact in Ionic

Hope you're doing well! I'm encountering an issue with Ionic Contacts. Currently, I'm able to retrieve all the contacts using my code but I need help extracting all the phone numbers associated with each contact. For example, if John has 3 ...

Guidance on showcasing the current day's weekday name through TypeScript

I am perplexed about how to begin in TypeScript after successfully setting up the display using gate. ...

Issue: Unhandled promise rejection: BraintreeError: The 'authorization' parameter is mandatory for creating a client

I'm currently working on integrating Braintree using Angular with asp.net core. However, I've encountered an issue that I can't seem to solve. I'm following this article. The version of Angular I'm using is 14, and I have replicate ...

Having trouble accessing an Angular app through express and Heroku?

I'm fairly new to express, and I have an Angular single-page application that I'm attempting to deploy on Heroku at the following link: Unfortunately, all I see is a blank screen. This happens when I run my server.js file on port 8000 as well. H ...