title: Angular+Jasmine: Cannot spy on ActivatedRoute.queryParams
body: <p>How can I spy on a stubbed Angular Service method that is expected to return an Observable such as ActivatedRoute.queryParams. Here is my failing spec:</p>
  
  <pre><code>import { TestBed, ComponentFixture } from "@angular/core/testing";
  import { RivalChartComponent } from "./rival-chart-component";
  import { FormsModule, ReactiveFormsModule } from "@angular/forms";
  import { ActivatedRoute, RouterModule, Router } from "@angular/router";
  import { appRoutes } from "../app.routes";
  import { Observable } from 'rxjs/Observable';
  
  import 'rxjs/add/observable/of';
  
  describe('RivalChartComponent', () =&gt; {
  
      let component: RivalChartComponent;
      let fixture: ComponentFixture&lt;RivalChartComponent&gt;;
  
      beforeEach(() =&gt; {
          TestBed.configureTestingModule({
              declarations: [
                  RivalChartComponent
              ],
              imports: [FormsModule, ReactiveFormsModule],
              providers: [
                  {provide: ActivatedRoute, useValue: {queryParams: Observable.of({tags:'all'})}},
                  {provide: Router, useValue: {}}
              ]
          });
          fixture = TestBed.createComponent(RivalChartComponent);
          component = fixture.componentInstance;
      });
  
      it('should init selectedTags as empty string when no query parameter is set', () =&gt; {
          spyOn(fixture.debugElement.injector.get(ActivatedRoute), 'queryParams').and.returnValue(Observable.of({tags:null}));
          component.ngOnInit();
          expect(component.selectedTags.value).toEqual('')
      });
  
  });
  </code></pre>
  
  <p>And here is the corresponding source code: </p>
  
  <pre><code>import { Component, OnInit } from '@angular/core';
  import { Rival } from '../domain/rival';
  import { Route, ActivatedRoute, Router } from '@angular/router';
  import { FormGroup, FormControl } from '@angular/forms';
  import { CompairValidators } from '../validators/CompairValidators';
  
  @Component({
      moduleId: module.id,
      selector: 'cp-rival-chart',
      templateUrl: 'rival-chart.html'
  
  })
  export class RivalChartComponent implements OnInit {
  
      rivals: Array&lt;Rival&gt; = [];
      selectedTags: FormControl;
  
      constructor(private route: ActivatedRoute, private router: Router) {
          this.selectedTags = new FormControl('', CompairValidators.trimmedArrayMinLengthValidator(3,1));
      }
  
      ngOnInit() {
          this.updateInputFromQueryParams();
      }
  
      updateInputFromQueryParams() {
          this.route.queryParams.subscribe((params) =&gt; {
              this.selectedTags.patchValue(this.createInputTagsFromUrlTags(params.tags));
          });
      }
  
      createInputTagsFromUrlTags(tags: String): string {
          if (!tags) {
              return '';
          }
          const tagsArray = tags.split('+');
          const cleanedTags = tagsArray
              .filter((tag) =&gt; tag &amp;&amp; tag.trim());
          return '#' + cleanedTags.join(' #');
      }
  
  }
  </code></pre>
  
  <p>The code is expected to initialize the selectedTags FormControl from the query parameter named tags given by the ActivatedRoute service. When there is no query parameter given it should initialize selectedTags with an empty String. In order to test it I would like to spy on the ActivatedRoutes.queryParams method and return an empty String for the key tags but unfortunately it does not work. Instead the initially passed ActivatedRoute stub's Observable gets called which returns the String 'all' for the key tags. Consequently my test fails with the following message:</p>
  
  <blockquote>
    <p>Expected '#all' to equal ''.</p>
  </blockquote>
  
  <p>You might think why the ActivatedRoute stub does not directly return an empty or null string. That's because there are actually other tests in my spec which I did not add here in order to focus on my main problem.</p>
  
  <p>--- Update ---</p>
  
  <p>Turns out to be that <code>ActivatedRoute.queryParams</code> cannot be spied the regular way in jasmine since it is not a function but a property. One way to deal with that is to provide an Object for injection that contains the property <code>queryParams</code> with the desired value. Here are the updates to apply in my initial Code in order to make the test work:</p>
  
  <pre><code>  let activatedRoute: jasmine.SpyObj&lt;ActivatedRoute&gt;;
  
    beforeEach(() =&gt; {
      const activatedRouteSpy = {
        queryParams: of({ tags: null }),
        ...jasmine.createSpyObj("ActivatedRoute", [""]),
      };
      TestBed.configureTestingModule({
        declarations: [RivalChartComponent, RivalCardComponent, SortRivalsByVotesPipe, SpinnerComponent],
        imports: [FormsModule, ReactiveFormsModule, MatProgressSpinnerModule],
        providers: [
          {
            provide: ActivatedRoute,
            useValue: activatedRouteSpy,
          },
          ...
        ],
      });
      fixture = TestBed.createComponent(RivalChartComponent);
      component = fixture.componentInstance;
      activatedRoute = TestBed.get(ActivatedRoute);
    });
  
    it("should init selectedTags as empty string when no query parameter is set", () =&gt; {
      component.ngOnInit();
      expect(component.selectedTags.value).toEqual("");
    });
  </code></pre>