6

I'm trying to unit test a (angular 2) component with routerLink and routerLinkActive but all test cases fail with following error message. (Note: I have stubbed the router and other related dependencies. Here's the reference that I used for the same.).

Cannot read property 'subscribe' of undefined

I have also noticed that when routerLink and routerLinkActive are removed from my template, all test cases will run without any errors.

I think the error is caused by either RouterTestingModule or SharedModule(contains component for displaying and validating password input field). So I tried removing or adding them to find which is actually causing the problem. Here's what I observed.

  • I get 'Cannot read property 'subscribe' of undefined' if RouterTestingModule or SharedModule is present.
  • I do not get any error linked to router only if I remove both RouterTestingModule and SharedModules but the elements from the SharedModules would not get loaded in the component and some test cases still fail because of that.

TestBed configuration:

TestBed.configureTestingModule({

            imports: [CoreModule,SharedModule,RouterTestingModule],
            declarations: [ MyComponent,RouterLinkStubDirective,RouterOutletStubComponent ],
            providers: [
                FormBuilder,
                { provide: Router, useClass: RouterStub },
                { provide: ActivatedRoute, userClass: ActivatedRouteStub}
            ],
            schemas: [NO_ERRORS_SCHEMA]
        })
            .overrideComponent(MyComponent, {
                set: {
                    providers: [
                        {provide: AuthModel, useClass: MockAuthModel}    
                    ],
                }
            })
            .compileComponents();
    }));

ActivatedRouterStub:

@Injectable()
export class ActivatedRouteStub {

    // ActivatedRoute.params is Observable
    private subject = new BehaviorSubject(this.testParams);
    params = this.subject.asObservable();

    // Test parameters
    private _testParams: {};
    get testParams() { return this._testParams; }
    set testParams(params: {}) {
        this._testParams = params;
        this.subject.next(params);
    }

    // ActivatedRoute.snapshot.params
    get snapshot() {
        return { params: this.testParams };
    }
}

What changes should I make to solve the issue?

Sanju
  • 1,478
  • 2
  • 20
  • 41
  • Can you share the ActivatedRouteStub, sounds like you're not returning an Observable from your stub setups. Also what are your test fixtures? Do you get the error before even the tests are run? – KlsLondon Sep 20 '17 at 11:57
  • @KlsLondon Actually my test case fails with following error: _Failed: Error in ./myComponent class myComponent - inline template:80:21 caused by: Cannot read property 'subscribe' of undefined_. Also I have updated the question with ActivatedRouterStub – Sanju Sep 20 '17 at 12:15
  • @KlsLondon Since the most simple test cases such as `it('Should create component',() => { expect(component).toBeDefined(); });` is failing, I think the errors appear before test cases are run. – Sanju Sep 20 '17 at 12:24
  • this.testParams would be undefined unless you call the setter of that property somewhere before your test runs? – KlsLondon Sep 20 '17 at 13:26
  • @KlsLondon Yes. I've called setter in beforeEach() `let activatedRoute = new ActivatedRouteStub(); activatedRoute.testParams = { url: this.requestURL };` – Sanju Sep 20 '17 at 13:44
  • @KlsLondon Should we provide mocked router if we have imported RouterTestingModule? I found a solution which said there is no need to provide it if RouterTestingModule is present. Since I have routerLink in my template and it [requires](https://stackoverflow.com/a/42039920/8558515) real Router, I tried to remove the Router providers. Now I get `Cannot read property 'outlets' of null`. – Sanju Sep 21 '17 at 06:28
  • Did you solve this issue. I have the same problem – user1188867 Sep 21 '17 at 08:54
  • @user1188867 I have answered the solution that works for me! – Sanju Sep 21 '17 at 11:33

2 Answers2

4

I have managed to solve this issue. I'll list what changes I made to solve the issue.

  1. As I mentioned in the comments, routerLink works only on real router. So we need to include RouterTestingModule (include .withRoutes([{path: 'some/path',component: someComponent}]) if you plan to click on it while testing).

  2. We need not provide Router dependencies separately if we have added RouterTestingModule already. So I removed both Router and ActivatedRoute dependency that I provided. Now, the error changed from Cannot read property 'subscribe' of undefined to Cannot read property 'outlets' of null.

  3. I found out this error occurs when the routerLink is pointing to a null variable. In my case, the property called favoriteUrl that i assigned to routerLink was null because of the isolated testing environment. So I manually assigned a value to the property in beforeEach function and that solved the problem, atleast for me.

Thanks to everyone who tried to help! :-)

References:

https://stackoverflow.com/a/45916133/8558515

https://stackoverflow.com/a/42039920/8558515

Community
  • 1
  • 1
Sanju
  • 1,478
  • 2
  • 20
  • 41
0

Angular has no way of knowing that you want it to use the activatedRoute variable that you created yourself(as per comment) so it will create default one and use that in DI. Instead of the current setup for the ActivatedRoute dependency use { provide: ActivatedRoute, useValue: mockInstance} where mockInstance is a mock of ActivatedRoute, you can create that with jasmine.createSpyObj

KlsLondon
  • 1,160
  • 1
  • 9
  • 18
  • Did not work. Unfortunately same error. I created a class `class MockActivatedRoute { params = { subscribe: jasmine.createSpy('subscribe') .and .returnValue(Observable.of({url: this.requestUrl})) } }` and provided the class in ActivatedRoute provider. – Sanju Sep 20 '17 at 14:30
  • Hard to help you when don't have all the code, maybe reproduce the error in jsFiddle project? – KlsLondon Sep 21 '17 at 10:26
  • Thank for the help! I got the solution. :-) – Sanju Sep 21 '17 at 11:31