6

I have a components that gets a value from the params property of a ActivatedRoute.

The components looks like:

......
  constructor(public userRegistration: UserRegistrationService, public userLogin: UserLoginService,
              public router: Router, public route: ActivatedRoute) {
  }

  ngOnInit() {
    this.verificationCode = new FormControl('', [Validators.required]);
    this.confirmationCodeForm = new FormGroup({verificationCode: this.verificationCode});

    //****************************** 
    this.sub = this.route.params.subscribe(params => {
      this.email = params['userId'];
    });
   //******************************
    this.errorMessage = null;
  }
 ......

The test provides an ActivatedRoute as "useValue" that mocks the class. The test looks like:

describe('ConfirmRegistrationComponent', () => {
  let component: ConfirmRegistrationComponent;
  let fixture: ComponentFixture<ConfirmRegistrationComponent>;
  let userRegistrationService: UserRegistrationService;
  let userLoginService: UserLoginService;
  let router: Router;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [ReactiveFormsModule, FormsModule, RouterTestingModule, HttpClientModule],
      declarations: [ConfirmRegistrationComponent],
      providers: [UserRegistrationService, UserLoginService, {

        provide: ActivatedRoute,
        useValue: {'params': Observable.from([{userId: 1}])}

      }, ImageService, UserService, CognitoUtil,
        {
          provide: Router, useClass: class {
            navigate = jasmine.createSpy('navigate');
          }
        }]
    }).compileComponents();

    fixture = TestBed.createComponent(ConfirmRegistrationComponent);
    component = fixture.componentInstance;
    userRegistrationService = TestBed.get(UserRegistrationService);
    userLoginService = TestBed.get(UserLoginService);
    spyOn(userLoginService, 'isAuthenticated').and.callFake(function () {

    });
    router = TestBed.get(Router);
    fixture.detectChanges();
    component.ngOnInit();
  }));

  it('should create', () => {
  });
});

When I run the test, I get the next error:

Failed: Cannot read property 'subscribe' of undefined
TypeError: Cannot read property 'subscribe' of undefined
    at new RouterLinkWithHref node_modules/@angular/router/esm5/router.js:6099:1)

Could you help me please? Thanks!

AleGallagher
  • 1,745
  • 7
  • 30
  • 40

2 Answers2

8

When using the RouterTestingModule, you should not put a Router provider in the providers collection (either import RouterTestingModule or provide Router, but not both).

Additionally, I don't know if this will help, but I had a related problem in Angular 6 where I had to use this syntax:

providers: [
    { provide: ActivatedRoute, useValue: {
            paramMap: of( convertToParamMap( { userId: 1 } ) )
        }
    }
],

convertToParamMap is in @angular/Router

Beartums
  • 1,340
  • 1
  • 10
  • 15
  • Thank you for your answer @Beartums, but it's still not working. – AleGallagher Jun 14 '18 at 23:04
  • @AleGallagher, right. I thought that would b a longshot. The other suggestion is that this is an error with the router module. I see you are providing a router class even though you have imported the RouterTestingModule. I believe that means that RouterTestingModule is not being used. Can you try deleting the provided router class (don't have a router at all in the 'provides' collection, but still import the routerTestingModule). That should allow the RTM to deal wuth the RouterLinkWithHref message and you can spy on the router later. – Beartums Jun 15 '18 at 04:40
  • Thanks @Beartums ! It works deleting the provider Router class and leaving the use value of ActivatedRoute with : useValue: {'params': Observable.from([{userId: 1}])}. With your suggestion of using paramMap: of( convertToParamMap( { userId: 1 } ) ) the test return an error. Thanks a lot! – AleGallagher Jun 16 '18 at 04:44
  • Great. Glad It worked out. Thanks for the input and I'll change the answer to match the actual problem, Sorry for the wild goose chase with the parammap. – Beartums Jun 16 '18 at 09:41
  • 1
    Worked like a charm for me, with a Router subscription in component, not needed to call Router on specs, only this instance of ActivatedRoute. Curious. – Vitor Piovezam Oct 28 '20 at 05:52
1

The solution worked for me was the following (Angular 7 + Jest):

const mockActivatedRoute = {
  snapshot: {
    paramMap: {
      get() { return '1'; }
    }
  }
};

And just use it in providers:

      providers: [
        { provide: ActivatedRoute, useValue: mockActivatedRoute },
      ],
kli
  • 456
  • 3
  • 12