Vlad B.
Vlad B.

Reputation: 2937

Angular Unit Test + SignalR - Error: Failed to complete negotiation with the server: Error: Not Found'

I have Angular 9 project with Asp.Net core which uses SignalR. Everything is working perfectly fine, but I'm trying to figure out how to do proper unit testing on the component that its using signalr service inside Angular.

library used on frontend: "@aspnet/signalr": "^1.1.4"

This happens only on unit testing. The error that I'm getting is

Error: Failed to complete negotiation with the server: Error'
    Chrome 91.0.4472 (Windows 10.0.0): Executed 1 of 1 SUCCESS (0.304 secs / 0.274 secs)
Error: Failed to start the connection: Error'
    Chrome 91.0.4472 (Windows 10.0.0): Executed 1 of 1 SUCCESS (0.304 secs / 0.274 secs)
    ERROR: 'Error'
    Chrome 91.0.4472 (Windows 10.0.0): Executed 1 of 1 SUCCESS (0.304 secs / 0.274 secs)
    ERROR: 'Error'
    Chrome 91.0.4472 (Windows 10.0.0): Executed 0 of 1 SUCCESS (0 secs / 0 secs)
    Chrome 91.0.4472 (Windows 10.0.0): Executed 1 of 1 SUCCESS (0.3 secs / 0.271 secs)

My signalr.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HubConnection } from '@aspnet/signalr';
import * as signalR from '@aspnet/signalr';
import { Observable } from 'rxjs';
import { SignalRConnectionInfo } from './signalr-connection-info.model';
import { Subject } from 'rxjs';
import { ApiService } from '../../api.service';

    @Injectable()
    export class SignalRService {
    
        private hubConnection: HubConnection;
        private apiHubConnection: HubConnection;
        modelChange: Subject<any> = new Subject();
        change: Subject<any> = new Subject();
        constructor(private http: HttpClient, private apiService: ApiService, private authService: AuthenticationService) {}
    
        private getConnectionInfo(): Observable<SignalRConnectionInfo> {
            return this.http.get<SignalRConnectionInfo>(this.apiService.functionUrl + 'negotiate');
        }
    
        init() {
            this.initApiHub();
            this.getConnectionInfo().subscribe(info => {
                const options = {
                    accessTokenFactory: () => info.accessToken
                };
    
                this.hubConnection = new signalR.HubConnectionBuilder()
                    .withUrl(info.url, options)
                    .configureLogging(signalR.LogLevel.Information)
                    .build();
    
                this.hubConnection.start().catch(err => console.error(err.toString())); // this is the error where it happens. 
    
                this.hubConnection.on('NotifyModelRunStatus', (data: any) => {
                    this.modelChange.next(data);
                });
    
                this.hubConnection.on('NotifyMoveOrderStatus', (data: any) => {
                    this.modelChange.next(data);
                    this.change.next(data);
                });
            });
        }
        initApi() {
            const options = {
                accessTokenFactory: () => this.authService.getAccessToken()
            };
            this.apiHubConnection = new signalR.HubConnectionBuilder()
                .withUrl(this.apiService.apiBaseUrl, options)
                .configureLogging(signalR.LogLevel.Error)
                .build();

            this.apiHubConnection.start()
                .catch(err => console.error(err ? err.toString() : err));
    
            // Add different SignalR methods from API signalR below
            this.apiHubConnection.on('Some change', (data: any) => {
                this.change.next(data);
            });
    
            this.apiHubConnection.on('Some change', (data: any) => {
                this.change.next(data);
            });

        }
    }

The component unit test where error is shown:

describe('MyComponent', () => {
    let component: MyComponent;
    let fixture: ComponentFixture<MyComponent>;
    let signalRService: SignalRService;
    
    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [
                RouterTestingModule,
                CommonModule,
                HttpClientTestingModule,
                HttpClientModule,
                ReactiveFormsModule,
                FormsModule,
                BrowserAnimationsModule,
            ],
            declarations: [MyComponent],
            providers: [
                SignalRService,
                ApiService,
                HttpClient,
            ],
        }).compileComponents();
        
        apiService = TestBed.inject(ApiService);
    }));

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

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

});

I guess the issue is that I'm not mocking the signalr service in test component.

Upvotes: 1

Views: 1113

Answers (1)

Roy.L.T
Roy.L.T

Reputation: 183

The concept of unit testing means you are only testing your component in its test, not other services. Hence you only need to assert the methods in the service has been invoked.

In your case, the "providers" in your test configuration should be a mock object instead of a real service.

describe('MyComponent', () => {
    let component: MyComponent;
    let fixture: ComponentFixture<MyComponent>;
    let signalRServiceSpy: jasmine.SpyObj<SignalRService>; // changed

    beforeEach(() => { // changed
        signalRServiceSpy = jasmine.createSpyObj('SignalRService', ['yourMethodInService']); // added
        TestBed.configureTestingModule({
            imports: [
                RouterTestingModule,
                CommonModule,
                HttpClientTestingModule,
                HttpClientModule,
                ReactiveFormsModule,
                FormsModule,
                BrowserAnimationsModule,
            ],
            declarations: [MyComponent],
            providers: [
                { provide: SignalRService, useValue: signalRServiceSpy }, // changed
                ApiService, // this should also use a spy object
                HttpClient, // you might not need this
            ],
        }).compileComponents();
    
        apiService = TestBed.inject(ApiService);
    });

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

Upvotes: 0

Related Questions