Testing an Angular 6 component that includes a dynamic variable rendered in HTML

Hello! I'm a newcomer to Angular testing and I've hit a roadblock. The issue lies with the username variable within a component that is responsible for displaying the logged-in username on an HTML page. Take a look at the test file below:

describe('HeaderComponent', () => {
  let component: HeaderComponent;
  let fixture: ComponentFixture<HeaderComponent>;
  let app;
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ HeaderComponent, DialogListConfigurationComponent, DialogConfirmNewConfigurationComponent ],
      providers: [ MessageService, JwtHelperService, AuthService ],  
      imports: [ DialogModule, MenubarModule, FontAwesomeModule, RouterTestingModule, TreeModule, HttpClientTestingModule, JwtModule.forRoot({
        config: {
          tokenGetter: () => {
            return sessionStorage.getItem(environment.tokenName);
          },
          blacklistedRoutes: [
            '/api/login',
          ]
        }
      }), ]
    })
    .compileComponents();
  }));

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

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
The relevant component code is as follows:
export class HeaderComponent implements OnInit {
  @Output() sidebarEvent = new EventEmitter();
  @Output() clickOkButtonListConfigurationsEvent = new EventEmitter();
  username: String = this.authService.decodeToken.username;
  items = [] as MenuItem[];
  dialogListConfigurationVisible: boolean = false;
  faUserCircle = faUserCircle;
  defaultConfigurations: Configuration[];
  configurationCreated: boolean = false;
  dialogConfirmCreationVisible: boolean = false;
  constructor(private configurationService: ConfigurationService, private authService: AuthService) { }

  ngOnInit() {
  ...
  }
You can find the auth service method here:
get decodeToken(): Jwt {
  return this.jwtHelper.decodeToken(this.getSessionToken);
}
The current issue causing the should create test to fail is
TypeError: Cannot read property 'username' of null
. Any ideas?

Answer №1

When testing a component, it is necessary to simulate the behavior of its AuthService by using a mock version.

This can be achieved by providing an object in the initialization code of the module.

TestBed.configureTestingModule({
  declarations: [ HeaderComponent, DialogListConfigurationComponent, DialogConfirmNewConfigurationComponent ],
  providers: [
    MessageService,
    JwtHelperService,
    {provide: AuthService, useClass: AuthServiceMock} ],  
  imports: [ DialogModule, MenubarModule, FontAwesomeModule, RouterTestingModule, TreeModule, HttpClientTestingModule, JwtModule.forRoot({
    config: {
      tokenGetter: () => {
        return sessionStorage.getItem(environment.tokenName);
      },
      //Exclude this URL from JWT (doesn't add the authentication header)
      blacklistedRoutes: [
        '/api/login',
      ]
    }
  }), ]
})
.compileComponents();

The AuthServiceMock should adhere to the same interface as the original AuthService. It could look something like this:

export class AuthServiceMock {
  // ...
  decodeToken: {
    username: 'testUser'
  };
}

Prior to creating the component, configure the mock class unless a default value is set inside the mock.

NOTE: While it's possible to skip creating a mock and directly manipulate the values, this approach might compromise the separation of concerns within your tests.

beforeEach(() => {
    let authServiceMock: AuthServiceMock = TestBed.get(AuthService);
    authServiceMock.decodeToken = { username: 'anotheruser' };

    fixture = TestBed.createComponent(HeaderComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

By following these steps, the HeaderComponent should successfully execute the

this.authService.decodedToken.username
line.

It is advisable to mock dependencies that rely on external data sources for consistency in testing.

If certain dependencies of the original AuthService are not essential for testing purposes, such as the JwtHelperService, they can be excluded from the test module's provider list.

Answer №2

After reading the solution provided by @Thatkookooguy, I realized the issue and decided to include these two lines within the beforeEach function to avoid using a mock object:

const authService: AuthService = TestBed.get(AuthService);
authService.saveToken("token string");

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

Having trouble with loading JavaScript during ng build --prod process

The JavaScript file I'm using for multiple emails (multiple_emails.js plugin) works well with ng serve. Here is my code: (function( $ ){ $.fn.multiple_emails = function(options) { // Default options var defaults = { ...

When working with JSON in Angular, the JSON pipe may display an empty string for values that are "undefined"

When utilizing the json pipe within Angular, it displays a blank for any undefined values. <pre>{{undefined | json}}</pre> The output on the DOM is as follows: <pre></pre> This behavior differs from the JSON stringify function. ...

Exploring the nested component structure in Angular4 (RC5)

Stepping into the world of Angular and stumbling upon an outdated tutorial meant for Angular 2 (yeah, I know). Angular4: @NgModule({ imports: [ BrowserModule ], declarations: [ AppComponent , MyChildComponent ], bootstrap: [ AppComponent ] }) It a ...

Implement an overflow property at the end of an Angular 2 animation

To create a smooth slide in and out animation, I have implemented the following code: animations: [ trigger('assignState', [ state('maximize', style({ height: '*', })), ...

When utilizing Ionic with Angular, it is difficult to access the hardware back button on mobile devices that have buttons located within the display/screen

When trying to access the hardware back button in my app, I encountered an issue where I couldn't produce an alert message to the user before the app closed. After posting a question on Stack Overflow (link of the question) and receiving help from the ...

Unable to display complete content of Ionic 3 webpage

Trying to print a specific part of an Ionic page which contains a long list of items. However, encountering an issue where the entire list doesn't fit on one page and requires scrolling down to view all items. Expecting the print dialog, triggered by ...

What is preventing the tree shaking of "providers" within components or modules?

I understand that in order for a service to be tree-shakable, it must use providerIn. @Injectable({ providedIn: 'root' }) export class TestProviderInService { .... } However, I am curious as to why service registrations in the "providers" of ...

The current date is indicating that the date string provided is invalid for interpretation by dayjs()

Have you tried using DayJs? If you're working on the browser, specifically with Firefox + Vue + typescript, here's my date string: 2021-02-05 12:00 AM However, when I include the AM/PM in the code like this: const dateObj: any = dayjs('2 ...

The numerical value of zero in React Native TypeScript is being interpreted as NaN

When attempting to map an array in React Native (Android) and use its values or keys as data for return, I encountered an issue where the value 0 is read as NaN. This problem also arose when utilizing a typescript enum. The versions I am using are: typesc ...

If I exclusively utilize TypeScript with Node, is it possible to transpile it to ES6?

I am developing a new Node-based App where browser-compatibility is not a concern as it will only run on a Node-server. The code-base I am working with is in TypeScript. Within my tsconfig.json, I have set the following options for the compiler: { "inc ...

Position components in Angular 2 based on an array's values

Hello all, I am a beginner in terms of Angular 2 and currently facing some obstacles. My goal is to create a board for a board game called Reversi, which has a similar layout to chess but with mono-color pieces. In order to store the necessary information, ...

What is the process for obtaining the Angular.json file for my NX Workspace?

Looking to develop a fresh Angular web application within my NX Workspace, with plans to eventually convert it for iOS and Android using Capacitor. After setting up the nx monorepo, I proceeded to generate a new Angular application by running the command ...

Displaying rows in a mat-table based on a certain condition

Is there a way to only display data in the table if the status is 'done'? I attempted to remove the status, but it still shows the row. Any suggestions on how to achieve this? data { equipmentOrdered: 'laptop', qty: 1, s ...

Update the configuration to change the localization code for Angular to a different language

Getting my angular application ready for localization is a top priority. I need to make sure that the different languages are working seamlessly before publishing. { "$schema": "./node_modules/@angular/cli/lib/config/schema.json", ...

Top tips for accessing and modifying an immutable object within a component retrieved from an RXJS/NGRX store in Angular

This week we successfully updated our Angular v9 app to v11 and RXJS v6.6 with minimal issues. However, due to the store being in freeze mode, we are encountering errors when trying to update the store in certain areas of our code. While most of the issue ...

Creating an overlay that dynamically renders components

Trying to create a service that displays a spinner progress while loading, but encountering an error in the console. Here is the service: @Injectable({ providedIn: 'root' }) export class LoadingLockDataService { public message: string; pu ...

Encountering a CORS problem following a successful API request within an ABP application

Access to XMLHttpRequest at 'serversiteurl//api/services/app/QklyProcessEngine/CallProcess' from origin 'clientsiteurl' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resou ...

Unable to successfully install Angular CLI through npm

Having trouble installing Angular CLI with the command npm install @angualr/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="3f5c53567f0e1108110a">[email protected]</a>. I encountered the error shown in the screen ...

In a situation where Typescript fails to provide enforcement, how can you effectively indicate that a function is not defined for specific value(s)?

If I were to utilize Typescript to create a function called mean that calculates the mean of an array of numbers, how should I handle the scenario where the array is empty? Enforcing that an array must be non-empty can be inconvenient, so what would be th ...

Typescript is being lenient with incorrect use of generics, contrary to my expectations of error being thrown

Encountered a puzzling Typescript behavior that has left me confused. Take a look at the following code snippet: interface ComponentProps<T> { oldObject: T } function Component<T>({ oldObject }: ComponentProps<T>) { const newObject = ...