In my ponent I have a child ponent that looks like this:
<child-ponent #childComponent></childComponent>
In my parent ponent I then access this child ponent using @ViewChild and the read
parameter to get the ElementRef, and not the ponent reference. I need the ElementRef to ensure I can get some properties from nativeElement
that I need. So it's like this:
export class ParentComponent {
@ViewChild('childComponent', { read: ElementRef }) public childComponent: ElementRef;
public position: string;
// some way down the code
private someMethod() {
if (this.childComponent.nativeElement.offsetLeft > 500) {
this.position = 'left';
} else {
this.position = 'right';
}
}
}
So this works for the application, however I am writing the tests and mocking the child ponent, like this:
@Component({
selector: 'child-ponent',
template: ''
})
class ChildComponentMockComponent {
private nativeElement = {
get offsetLeft() {
return 600
}
};
}
beforeEach(async(() => TestBed.configureTestingModule({
imports: [ ... ],
declarations: [ ParentComponent, ChildComponentMockComponent ],
providers: [ ... ],
schemas: [ NO_ERRORS_SCHEMA ]
})pileComponents()));
it('should have the correct position, based on position of child-ponent', () => {
spyOn(ponent, 'someMethod');
expect(ponent.someMethod).toHaveBeenCalled();
expect(ponent.position).toBe('left');
});
So the test will pile the ponent, and use the mocked child ponent values as the proper value and pute the value of this.position
, which is then asserted in the test.
However, when the { read: ElementRef }
parameter is set, the mock gets pletely ignored by the TestBed, even though it's being added in the declarations array. If I remove { read: ElementRef }
, the mock is used in the test and it passes. But then my application doesn't work, as it is now getting the ponent reference, where the nativeElement
property doesn't exist, rather than the element reference.
So how do I get the ElementRef in my application and then in my test use the mock ponent?
In my ponent I have a child ponent that looks like this:
<child-ponent #childComponent></childComponent>
In my parent ponent I then access this child ponent using @ViewChild and the read
parameter to get the ElementRef, and not the ponent reference. I need the ElementRef to ensure I can get some properties from nativeElement
that I need. So it's like this:
export class ParentComponent {
@ViewChild('childComponent', { read: ElementRef }) public childComponent: ElementRef;
public position: string;
// some way down the code
private someMethod() {
if (this.childComponent.nativeElement.offsetLeft > 500) {
this.position = 'left';
} else {
this.position = 'right';
}
}
}
So this works for the application, however I am writing the tests and mocking the child ponent, like this:
@Component({
selector: 'child-ponent',
template: ''
})
class ChildComponentMockComponent {
private nativeElement = {
get offsetLeft() {
return 600
}
};
}
beforeEach(async(() => TestBed.configureTestingModule({
imports: [ ... ],
declarations: [ ParentComponent, ChildComponentMockComponent ],
providers: [ ... ],
schemas: [ NO_ERRORS_SCHEMA ]
}).pileComponents()));
it('should have the correct position, based on position of child-ponent', () => {
spyOn(ponent, 'someMethod');
expect(ponent.someMethod).toHaveBeenCalled();
expect(ponent.position).toBe('left');
});
So the test will pile the ponent, and use the mocked child ponent values as the proper value and pute the value of this.position
, which is then asserted in the test.
However, when the { read: ElementRef }
parameter is set, the mock gets pletely ignored by the TestBed, even though it's being added in the declarations array. If I remove { read: ElementRef }
, the mock is used in the test and it passes. But then my application doesn't work, as it is now getting the ponent reference, where the nativeElement
property doesn't exist, rather than the element reference.
So how do I get the ElementRef in my application and then in my test use the mock ponent?
Share Improve this question edited Dec 6, 2017 at 15:26 Tom Oakley asked Dec 6, 2017 at 11:27 Tom OakleyTom Oakley 6,44312 gold badges48 silver badges75 bronze badges 4- can you add the test case which you wrote so i can help you to fix it – Aravind Commented Dec 6, 2017 at 11:49
- Hi @Aravind, the property isn't actually used in the test, sorry for being ambiguous. But the test piles the ponent using the mocked child-ponent instead of the proper one, then in the application code for the ponent, it uses the value I have set in the mock instead of anything else. The app code sets a variable (true/false) based on the position of the child ponent, which I then assert in the test. It's quite confusing, but I've added a little more detail to my original post. – Tom Oakley Commented Dec 6, 2017 at 11:52
-
when you use read elementRef, it's not using ponent instance, so why do you declare the
nativeElement
property on the ponent instance? do you want to mockelementRef
? – Max Koretskyi Commented Dec 7, 2017 at 10:40 -
nativeElement
isn't available on the ponent instance. Ideally yes I'd mock elementRef but I tried to do that and it didn't appear to work. – Tom Oakley Commented Dec 7, 2017 at 10:43
1 Answer
Reset to default 2I have fixed this by changing the architecture of the app. The child ponent now finds it's own offsetLeft property, and then puts it into an output EventEmitter to be picked up the parent ponent.
export class ChildComponent implements AfterViewInit {
@Output() offsetPosition: EventEmitter<number> = new EventEmitter<number>();
constructor(private el: ElementRef) {}
public ngAfterViewInit() {
this.offsetPosition.emit(this.el.nativeElement.offsetLeft);
}
}
export class ParentComponent implements AfterViewInit {
public childComponentOffset: number;
public ngAfterViewInit() {
setTimeout(() => {
// this.childComponentOffset is available
// needs to be in setTimeout to prevent ExpressionChangedAfterItHasBeenCheckedError
// more info: https://blog.angularindepth./everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4
}
}
public getChildComponentOffset(position: number): void {
this.childComponentOffset = position;
}
}
And then in the HTML, you just define the child ponent with output variable and method:
<child-ponent (offsetPosition)="getChildComponentOffset($event)"></child-ponent>
In the test, I then mock ElementRef for the child ponent and use it as a provider.
const mockElementRef: any = {
get offsetLeft() {
return position;
}
};
beforeEach(async(() => TestBed.configureTestingModule({
imports: [ ... ],
declarations: [ ParentComponent ],
providers: [
{ provide: ElementRef, useValue: mockElementRef }
],
schemas: [ NO_ERRORS_SCHEMA ]
}).pileComponents()));
it('should have the correct position, based on position of child-ponent', (done) => {
ponent.getChildComponentOffset(600);
setTimeout(() => expect(ponent.position).toBe('left'));
done();
});
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745187717a4615715.html
评论列表(0条)