I am unit testing a component that is used to edit an object. The object has an unique id
that is used in order to grab the specific object from an array of objects that are hosted in a service. The specific id
is procured through a parameter that is passed via routing, specifically through the ActivatedRoute
class.
The constructor is as follows:
constructor(private _router:Router, private _curRoute:ActivatedRoute, private _session:Session) {}
ngOnInit() {
this._curRoute.params.subscribe(params => {
this.userId = params['id'];
this.userObj = this._session.allUsers.filter(user => user.id.toString() === this.userId.toString())[0];
I want to run basic unit tests on this component. However, I am not sure as to how I can inject the id
parameter, and the component needs this parameter.
By the way: I already have a mock for the Session
service, so no worries there.
Test Cases for ActivatedRoute it('getDataByNameAndID test case ', () => {route.snapshot.params.id = '2';route.snapshot.params.name = 'testParamChanged';fixture = TestBed. createComponent(MyComponent);component = fixture. componentInstance;fixture. detectChanges();expect(component.name).
ActivatedRoutelink. Provides access to information about a route associated with a component that is loaded in an outlet. Use to traverse the RouterState tree and extract information from nodes.
To test the resolver we need to render RouterOutlet . const fixture = MockRender(RouterOutlet); Additionally, we also need to properly customize mocked services if the resolver is using them to fetch data. const dataService = TestBed.
Since ActivatedRoute can be reused, ActivatedRouteSnapshot is an immutable object representing a particular version of ActivatedRoute . It exposes all the same properties as ActivatedRoute as plain values, while ActivatedRoute exposes them as observables.
The simplest way to do this is to just use the useValue
attribute and provide an Observable of the value you want to mock.
RxJS < 6
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
...
{
provide: ActivatedRoute,
useValue: {
params: Observable.of({id: 123})
}
}
RxJS >= 6
import { of } from 'rxjs';
...
{
provide: ActivatedRoute,
useValue: {
params: of({id: 123})
}
}
In angular 8+ there is the RouterTestingModule
, which you can use in order to have access to the ActivatedRoute
or Router
of the component. Also you can pass routes to the RouterTestingModule
and create spies for the requested methods of route.
For example in my component I have:
ngOnInit() {
if (this.route.snapshot.paramMap.get('id')) this.editMode()
this.titleService.setTitle(`${this.pageTitle} | ${TAB_SUFFIX}`)
}
And in my test I have:
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ProductLinePageComponent ],
schemas: [NO_ERRORS_SCHEMA],
imports: [
RouterTestingModule.withRoutes([])
],
})
.compileComponents()
}))
beforeEach(() => {
router = TestBed.get(Router)
route = TestBed.get(ActivatedRoute)
})
and later in the 'it' section:
it('should update', () => {
const spyRoute = spyOn(route.snapshot.paramMap, 'get')
spyRoute.and.returnValue('21')
fixture = TestBed.createComponent(ProductLinePageComponent)
component = fixture.componentInstance
fixture.detectChanges()
expect(component).toBeTruthy()
expect(component.pageTitle).toBe('Edit Product Line')
expect(component.formTitle).toBe('Edit Product Line')
// here you can test the functionality which is triggered by the snapshot
})
In a similar way, I think you can test directly the paramMap
via the spyOnProperty
method of jasmine, by returning an observable or using rxjs marbles. It might save some time & also it does not require to maintain an extra mock class.
Hope that it is useful and it makes sense.
I have figured out how to do this!
Since ActivatedRoute
is a service, a mock service for it can be established. Let's call this mock service MockActivatedRoute
. We will extend ActivatedRoute
in MockActivatedRoute
, as follows:
class MockActivatedRoute extends ActivatedRoute {
constructor() {
super(null, null, null, null, null);
this.params = Observable.of({id: "5"});
}
The line super(null, ....)
initializes the super class, which has four mandatory parameters. However, in this instance, we need nothing from any of these parameters, so we initialize them to null
values. All we need is the value of params
which is an Observable<>
. Therefore, with this.params
, we override the value of params
and initialize it to be the Observable<>
of the parameter on which the test subject is relying.
Then, as any other mock service, just initialize it and override the provider for the component.
Good luck!
Here is how I tested it in angular 2.0 latest...
import { ActivatedRoute, Data } from '@angular/router';
and in Providers section
{
provide: ActivatedRoute,
useValue: {
data: {
subscribe: (fn: (value: Data) => void) => fn({
yourData: 'yolo'
})
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With