A. Duff
A. Duff

Reputation: 4309

How to mock ActivatedRouteSnapshot when unit testing a Resolver

I want to write a unit test for my resolver, which needs to take ActivatedRouteSnapshot in its constructor like so:

export class MyResolver {
  constructor () {
    // ...
  }

  resolve (private route: ActivatedRouteSnapshot) {
    callFoo(route.params.val);
  }
};

But in my unit test, what's the best way to provide an activated route snapshot with mock data? When I try to create an object with just the properties I need, I get an error that it cannot be cast to ActivatedRouteSnapshot:

it('should call foo', inject([MyResolver], async (myResolver: MyResolver) => {
  const mockRoute = {
    params: {
      val: '1234'
    };
  };

  sinon.spy(callFoo);
  myResolver.resolve(mockRoute); // This is the line that errors
  expect(callFoo.calledWith('1234')).to.be.true;
}));

Error:

Type '{ params: { val: string; }; }' cannot be converted to type 'ActivatedRouteSnapshot'.

How can I provide a mock ActivatedRouteSnapshot to pass to my resolver?

Upvotes: 24

Views: 24866

Answers (6)

ismaestro
ismaestro

Reputation: 8267

You have to provide the active route like this:

import {TestBed} from '@angular/core/testing';
import {SomeResolver} from './some.resolver';
import {ActivatedRoute, convertToParamMap} from '@angular/router';
import {of} from 'rxjs';

describe('SomeResolver', () => {
  let someResolver: SomeResolver;
  let route: ActivatedRoute;

  TestBed.configureTestingModule({
    providers: [
      {
        provide: ActivatedRoute,
        useValue: {snapshot: {paramMap: convertToParamMap({id: 'one-id'})}}
      },
      SomeResolver
    ]
  });

  beforeEach(() => {
    heroResolver = TestBed.get(HeroResolver);
    route = TestBed.get(ActivatedRoute);
  });

  it('should resolve', (() => {
    someResolver.resolve(route.snapshot);
  }));
});

Upvotes: 16

tanina soualah
tanina soualah

Reputation: 21

import createSpyObj = jasmine.createSpyObj;

let route = createSpyObj('Route', ['']);
route.params = {
  nameOfParam: 'test'
}

Upvotes: 2

James Riall
James Riall

Reputation: 41

Note you can also simply cast your object as an ActivatedRouteSnapshot if you like:

const mockRoute = {paramMap: convertToParamMap({'id': '123'})} as 
    ActivatedRouteSnapshot;

resolver.resolve(mockRoute);

Upvotes: 2

Joe H
Joe H

Reputation: 123

for my purposes, this was the easiest thing to do (which also allowed me to write to the 'parent' property which is normally read-only:

const route = Object.assign({}, ActivatedRouteSnapshot.prototype, {
  params: {
    myParam: 'some_value'
  },
  parent: {
    params: {
      myParentParam: 'some_other_value'
    }
  }
});

then you just call your resolver with 'route' like so:

it('should do something with my activated route params', (done) => {
const route = Object.assign({}, ActivatedRouteSnapshot.prototype, {
  params: {
    myParam: 'some_value'
  },
  parent: {
    params: {
      myParentParam: 'some_other_value'
    }
  }
});
service.resolve(route).subscribe(res => {
  expect( ... )
  done();
});
});

Upvotes: 1

Valeriy Katkov
Valeriy Katkov

Reputation: 40732

If the purpose is just to pass the mock to the resolver, it's not necessary to use createSpyObj, as suggested in other answers. Also, it would be better to add type safety to the solution:

const mock = <T, P extends keyof T>(obj: Pick<T, P>): T => obj as T;

it('should call foo', () => {
    const route = mock<ActivatedRouteSnapshot, 'params'>({
        params: {
            val: '1234'
        }
    });

    const resolver = createTheResolver();
    const resolverParams = resolver.resolve(route);
    ...
});

Upvotes: 3

Janneman96
Janneman96

Reputation: 505

I'm not sure if this is the prettiest solution, but you can mock the route like this:

let route = createSpyObj('Route', ['']);
route.params = {
  val: '1234'
}

Upvotes: 8

Related Questions