4

I'm trying to test a component with some links, such as:

<a routerLink="/contact">Contact</a>

Also, I already tried looking into this question, but it didn't solved the problem.

When I try the solution proposed by that question (code below), I keep getting this error

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

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

describe('...', () => {
    it('should navigate to "/contact" when clicking on link', async(inject([Location], (location: Location) => {
        fixture.debugElement.query(By.css('.menu')).children[1].nativeElement.click();
        fixture.detectChanges(); 
        fixture.whenStable().then(() => {
            console.log( location.path() );
        });
    })));
});

No provider for Location!

Then, if I tri to set providers: [Location] I get this error:

Failed: StaticInjectorError[LocationStrategy]:
StaticInjectorError[LocationStrategy]:
NullInjectorError: No provider for LocationStrategy!

I tried to spy on the Router, like this:

spyOn(router, 'navigate');
expect(router.navigate).toHaveBeenCalled();

But it fails the test with 'Never called'.

How can I solve this issue?

celsomtrindade
  • 4,501
  • 18
  • 61
  • 116
  • I think you are missing `CommonModule`. Side-note, that is some of the ugliest boilerplate code imaginable. – Aluan Haddad Nov 26 '17 at 14:03
  • @AluanHaddad It did solved the error, but the location.path() log always shows as blank `''` instead of the expected url. – celsomtrindade Nov 26 '17 at 14:15
  • I have no idea about that. Your setup looks suspicious though. You have a bunch of different synchronous and asynchronous initialization logic that you should look into. – Aluan Haddad Nov 26 '17 at 14:25
  • @AluanHaddad do you have any advice to improve this? I'm new to testing. – celsomtrindade Nov 26 '17 at 14:38
  • Personally, I would avoid testing components as much as possible by placing logic in service classes or functions that can be tested without framework intrusion. The given test seems to exercise the framework, not your code so I don't think it is needed. Specifically, nothing says that `Router.prototype.navigate` is and will always be the way the router responds to clicks on a `routerLink` but your test is testing that. – Aluan Haddad Nov 26 '17 at 14:44
  • @AluanHaddad one thing I noticed is that if I console.log the the element to be clicked, it does have routerlink (all lowercase) property, but doesn't have the href property, also mentioned on the other question I'm referencing on the question. Because I was trying to just test for the property on the element. – celsomtrindade Nov 26 '17 at 15:11

2 Answers2

2

As pointed by Aluan Haddad in the comments, I was missing the CommonModules on the imports.

After that, the error was gone, but I couldn't make it work. The reason is because I was using a MockModule, where I import all the common services, components, modules, etc.. Used across the tests, including the RouterTestingModule.

Removing the RouterTestingModule from the common MockModule and moving it direct into the imports, solved the issue.

// before
imports: [MockModule],

// after
imports: [RouterTestingModule.withRoutes(routing)],
celsomtrindade
  • 4,501
  • 18
  • 61
  • 116
  • I think many people will find this helpful. Can you shed some light on why it works this way? It seems very arbitrary. – Aluan Haddad Nov 27 '17 at 01:48
2

Since I searched for an answer for some time but couldn't find one, this is for everyone who couldn't resolve the "Expected spy ... but it was never called" issue with the answer above.

The routerLink directive of angular actually routes with 'navigateByUrl' instead of 'navigate', so your spy would have to look like this

spyOn(router, 'navigateByUrl')
JB17
  • 366
  • 5
  • 14