I have a problem in testing in Angular 2.
describe('Spinner Component', () => {
beforeEach(() => TestBed.configureTestingModule({
declarations: [SpinnerComponent]
}).compileComponents());
beforeEach(() => {
fixture = TestBed.createComponent(SpinnerComponent);
comp = fixture.componentInstance;
fixture.detectChanges();
});
it('Should Create a Spinner Component', () => {
fixture.detectChanges();
var compiled = fixture.debugElement.nativeElement;
expect(compiled).toBeTruthy();
});
it('Should not be running', () => {
fixture.detectChanges();
expect(comp.isRunning).toBe(false);
});
});
The code above shows that the Spinner Component in the test 'Should not be running'. I do not know what causes this. I get an error in the console (Please see below). I have created the component instance on the second before each as seen in the code but it states that it is undefined when ran on the second test case. I need help. I would really appreciate it. Thanks in advance.
Error in console
Related
I am writing a spec for an Angular component that displays a button that will navigate to another page. The component makes use of Router::navigate() but does not itself have a router outlet. A parent component has the outlet. In my spec, the test should confirm that clicking on the button routes to the correct path.
My current (broken) spec tries to use RouterTestingModule to provide a route to a DummyComponent. When the button is clicked in the spec I get the following error:
'Unhandled Promise rejection:', 'Cannot find primary outlet to load 'DummyComponent'', '; Zone:', 'angular', '; Task:', 'Promise.then', '; Value:', Error{__zone_symbol__error: Error{originalStack: 'Error: Cannot find primary outlet to load 'DummyComponent'
Obviously I am approaching this problem in the wrong manner. What is the correct way to test router navigation when the component does not have a router outlet?
The component (pseudo-code):
#Component({
template: `
Go to the <button (click)="nextPage">next page</button>
`
})
export class ExampleComponent {
public myId = 5;
constructor(private _router: Router);
public nextPage(): void {
this._router.navigate(['/example', this.myId]);
}
}
The spec. This does not work:
const FAKE_ID = 999;
describe('ExampleComponent Test', () => {
let exampleComponent: ExampleComponent;
let fixture: ComponentFixture<ExampleComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ DummyComponent ],
imports: [
RouterTestingModule.withRoutes([
{ path: 'example/:id', component: DummyComponent }
]);
]
});
fixture = TestBed.createComponent(exampleComponent);
exampleComponent = fixture.componentInstance;
});
it('should route to example/:id', inject([Router, Location], (router: Router, location: Location) => {
fixture.detectChanges();
exampleComponent.myId = FAKE_ID;
const LINK_BUTTON = fixture.debugElement.query(By.css('button'));
LINK_BUTTON.nativeElement.dispatchEvent(new Event('click'));
expect(location.path()).toEqual('/example/' + FAKE_ID);
});
});
There needs to be an outlet (<router-outlet>) for the DummyComponent. If the DummyComponent is a route being navigated to from the ExampleComponent, then the ExampleComponent should have the outlet. You also also need to add the ExampleComponent to the declarations`
#Component({
tempalte: `
<router-outlet></router-outlet>
<button (click)="nextPage">next page</button>
`
})
class ExampleComponent{}
declarations: [ ExampleComponent, DummyComponent ]
If you want to avoid having to set up this infrastructure just to test the route being navigated to, the better option might be to just mock the Router, and just check that the navigate method is called with the correct path.
beforeEach(()=>{
TestBed.configureTestingModule({
providers: [
{
provide: Router,
useValue: { navigate: jasmine.createSpy('navigate') }
}
]
})
})
With this, you don't need to configure an routing at all, as you're using a fake Router. Then in your test
it('should route to example/:id', inject([Router], (router: Router) => {
expect(router.navigate).toHaveBeenCalledWith(['/example', FAKE_ID]);
});
Attempting to run acceptance tests in Ember:
test('successful login', (assert) => {
Ember.run(() => {
visit('/signin');
fillIn('#email', 'validemail#server.com');
fillIn('#password', 'password');
click(':submit');
andThen(function() {
assert.equal(currentURL(), '/');
});
});
});
Occasionally (and seemingly randomly) yields the error:
"Global error: Error: Assertion Failed: You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in a run..."
I was able to get a working version:
test('successful login', (assert) => {
const done = assert.async();
Ember.run(() => {
visit('/signin').then(() => {
fillIn('#email', 'isaac#silverorange.com').then(() => {
fillIn('#password', 'keen').then(() => {
click(':submit').then(() => {
assert.equal(currentURL(), '/');
done();
});
});
});
});
});
});
However, if I include a second test making use of the same route (for an unsuccessful login), one of them almost always ends up with the error listed above.
I am wondering what I am not understanding about the run-loop, Ember.run, and how to test with async behavior. Any help or pointers towards a good resource would be greatly appreciated!
According to the guide, your code should be like this:
test('successful login', (assert) => {
visit('/signin');
fillIn('#email', 'validemail#server.com');
fillIn('#password', 'password');
click(':submit');
andThen(function() {
assert.equal(currentURL(), '/');
});
});
You don't need to add an Ember.run to your cases.
Most commonly, this problem occurs when you're doing something (asynchronously) in your application that isn't properly wrapped for Ember (by wrapped I mean executed inside the Ember run loop).
Most common causes
You attached an event handler to the DOM, either directly or with jQuery without wrapping interaction with the Ember application in Ember.run()
You executed a XHR (asynchronous), either directly or with jQuery without wrapping interaction with the Ember application inside the callback in Ember.run()
Generic fix
When you cause code execution that interacts with your application outside the runloop (XHR callback or event handlers) wrap that code with Ember.run().
Events:
Ember.$('div').on('mouseover',function() {
Ember.run(function() {
// Interaction with application
});
});
XHR/Ajax:
Ember.$.ajax({
success: function() {
Ember.run(function() {
// Interaction with application
});
}
});
Best practices
When working with DOM events:
Use component event handling (https://guides.emberjs.com/v2.11.0/components/handling-events/)
Use template actions (https://guides.emberjs.com/v2.11.0/templates/actions/)
When you want to do AJAX/XHR use ember-ajax (https://github.com/ember-cli/ember-ajax)
I have a simple Angular2 component that consists of the following
import { Component, OnInit, Input } from '#angular/core';
import {FooterLinksService} from './footer-links.service';
import { environment } from '../../environments/environment';
#Component({
selector: 'app-footer-links',
templateUrl: './footer-links.component.html',
styleUrls: ['./footer-links.component.css'],
providers: [FooterLinksService]
})
export class FooterLinksComponent implements OnInit {
constructor(private footerLinksService: FooterLinksService) {
let footerLinks = this.footerLinksService.LoadFooterLinks();
}
}
I am trying to write the unit tests with Jasmine for this. Now I want to mock the FooterLinksService, but most of the examples I have seen involve manually writing a FooterLinksServiceMock. Is there any other approach I can use which autogenerates the mock service like NSubStitute and I provide the expected returns values from footerLinksService.LoadFooterLinks
As mentioned by #JBNizet, you could just use a spy. What you would do is get the actual service inside the test, then you can spy on a method an return any arbitrary value, when that method is called. It might look something like
describe('component: FooterLinksComponent', () => {
let fixture;
let service;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
FooterLinksComponent
],
});
fixture = TestBed.createComponent(FooterLinksComponent);
service = fixture.debugElement.injector.get(FooterLinksService);
});
it('should change message returned', () => {
spyOn(service, 'LoadFooterLinks').and.returnValue('Hello new message');
fixture.detectChanges();
expect(fixture.componentInstance.message).toBe('Hello new message');
});
});
You will need to move the code where you access the service from inside the constructor into the ngOnInit. The reason for this is because
You are using #Component.providers, so the service will not be created until the component is created.
When the component is created, the constructor is already called. So this doesn't give time for you to set up the spy. When you use ngOnInit, the ngOnInit is not called until you call fixture.detectChanges()
I'm trying to get the basics of Angular2 test API and TestBed.compileComponents() is driving me nuts. Either I call it like this:
beforeEach( done => {
TestBed.configureTestingModule({
declarations: [MyComponent]
})
.compileComponents().then( () => {
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance();
});
done();
});
And then my component is undefined in my test (I believe since compileComponent is async, test is run before my var component gets a value)
Either like that (as describe in documentation):
beforeEach( async(() => {
TestBed.configureTestingModule({
declarations: [MyComponent]
}))
.compileComponents();
beforeEach( () => {
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance();
});
But then I get the error: This test module uses the component HomeComponent which is using a "templateUrl", but they were never compiled. Please call "TestBed.compileComponents" before your test.
Can anybody help on this ?
Forget to say I use webpack and RC6
Try this:
describe('FooComponent', function () {
let fixture: ComponentFixture<FooComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [FooComponent]
});
fixture = TestBed.createComponent(FooComponent);
fixture.detectChanges();
});
You don't need asynchronicity here.
Try this:
beforeEach( async( () => {
TestBed.configureTestingModule({
declarations: [MyComponent]
})
.compileComponents().then( () => {
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance();
});
}));
I faced the same issue. I think the problem is in your component template. If you use some other custom components, but didn't specify them in testing module, than Angular throws that error (of course very misleading).
So, you next options:
Specify all used components in TestBed configuration
As variation of that you may stub all components by corresponding mocks.
use NO_ERRORS_SCHEMA for shallow testing as described here https://angular.io/docs/ts/latest/guide/testing.html#shallow-component-test
Just to build on the answer by Zonkil I found to get this to work I had to actually set the fixture and create the component within the test. For example:
it('test the component renders',() => {
fixture = TestBed.createComponent(AppComponent);
comp = fixture.componentInstance;
fixture.detectChanges();
expect(comp).toBeDefined();
});
I'm trying to unit test a component that uses the Router, my code is in typescript. There are numerous recipies for injecting Router in a test spec, but none work for me, and some are not usable in the current version. I tried this:
beforeEach(() => {
addProviders([
MyComponent,
provideRouter([]),
provide(APP_BASE_HREF, { useValue: '/' }),
provide(ActivatedRoute, { useValue: {} })
]);
});
and got the error message
Error: Bootstrap at least one component before injecting Router.
When I try to mock the Router altogether:
class MockRouter {
public navigate() {}
}
beforeEach(() => {
addProviders([
MyComponent,
provide(Router, { useClass: MockRouter })
]);
});
the test suite stops altogether with the error message
TypeError: Attempting to configurable attribute of unconfigurable property.
In router_testing_module.d.ts, they suggest this:
beforeEach(() => {
configureModule({
modules: [RouterTestingModule],
providers: [provideRoutes(
[{path: '', component: BlankCmp}, {path: 'simple', component: SimpleCmp}])]
});
});
But the function configureModule does not seem to exist (yet? anymore?).
What can I do in this situation?