3

mycomponent.ts

import { Component, OnInit } from '@angular/core';
import {FormGroup,FormControl} from '@angular/forms'
import { DataServiceService } from './data-service.service';
import {combineLatest,Observable,pipe} from 'rxjs';
import {map,tap} from 'rxjs/operators';
import {Model} from './mode';
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

  constructor(private dataService: DataServiceService){}
  name = 'Angular';
  myForm: FormGroup;
  observableResult$: Observable<any>;

  ngOnInit(){
    this.myForm = new FormGroup({
      localId: new FormControl()
    })

  this.observableResult$ = combineLatest(
    this.myForm.get('localId').valueChanges,
    this.dataService.getDataFromURL(),
    (localIdSelected, dataFromAPI) => ({localIdSelected,dataFromAPI})).
    pipe(map(each => this.filterData(each.dataFromAPI,each.localIdSelected)));

    this.observableResult$.subscribe(value => {
      debugger
    })

  }
  filterData(dataFromAPI,localIDSelected){
    debugger
     return dataFromAPI.filter(item => item.userId > Number(localIDSelected));
    }

}

data.service.service.ts

import { Injectable } from '@angular/core';
import {HttpClient} from '@angular/common/http'
import {Model} from './mode';
import {Observable} from 'rxjs';
@Injectable()
export class DataServiceService {

  constructor(private http:HttpClient) { }

  getDataFromURL():Observable<Model>{
    return this.http.get<Model>('https://jsonplaceholder.typicode.com/todos');
  }

}

app.component.html

<form [formGroup]="myForm" >

<select formControlName="localId">
  <option>1</option>
  <option>2</option>
</select>

</form>

app.spec.ts

const spyFilter = spyOn(component as any, filterData).and.callThrough();

const constAPIData$ = staticDataServiceMock.getAPIData();
                    spyOn(staticDataServiceMock, 'getAPIData').and.returnValue(
                        observableOf(countryStaticData$)
                    );
component.myForm.get('localId').setValue(1);

component.observableResult$.subscribe(value => {
expect(value[0].id==21).toBeTrue();
});

staticDatamock.ts

export class StaticDataMock{

static getAPIData(): Observable<StaticDataElements[]> {
    return [
  {
    "userId": 1,
    "id": 1,
    "title": "delectus aut autem",
    "completed": false
  },
  {
    "userId": 1,
    "id": 2,
    "title": "quis ut nam facilis et officia qui",
    "completed": false
  },
  {
    "userId": 1,
    "id": 3,
    "title": "fugiat veniam minus",
    "completed": false
  },
  {
    "userId": 1,
    "id": 4,
    "title": "et porro tempora",
    "completed": true
  }];
  }
}

I have added my test case to cover the combineLatest operator anf filterData in app.spec.ts, but the required code fails. and my expectation to call filterData is failed. combineLatest will fire the event on a valueChange and get data from API. i can created mock and setValue inside spec file, still it is not working.

Mithun S
  • 408
  • 8
  • 20
  • What is your question? If you want someone to help you write test cases then you'll need to be very clear what exactly you are trying to test. You will also need to show a lot more detail, for example what `mock.getIds()` is and what it returns, if there are other mocks you have set up and their implementations, the details of the TestBed if you are using one, etc, etc. – dmcgrandle Nov 29 '18 at 17:45
  • @dmcgrandle: i have updated my question, please check – Mithun S Dec 01 '18 at 17:46
  • Thank you, we're getting closer. :) I am a little confused by the names of the various files. Is `app.component.html` the template for `mycomponent.ts`? Where is the file `mycomponent.spec.ts`? Also, please show the whole setup and TestBed implementation - in fact the whole spec file(s) other than the specs that are working and you don't need help with. You show the `StaticDataMock` class, but in the spec you call `staticDataServiceMock` - but do not show how (or even if) those two are related (my guess is you inject it in the TestBed [providers] array ...) – dmcgrandle Dec 01 '18 at 17:58

1 Answers1

3

Ok, to try and help you get going on this, I went ahead and set up a Stackblitz with the data you have provided so far. Here is the link.

I did a few things to get the test working (sort of).

  • I changed the method type of getAPIData() within the StaticDataMock class to public so you could call it from outside the class.
  • I had the method return an of(), turning the return value into an Observable of your data.
  • I guessed at your implementations of DataServiceService mocking, details in the Stackblitz.
  • I created a Jasmine spyObject to intercept the calls to getDataFromURL() in the service and return the observable you had created with StaticDataMock.
  • I rearranged the order of how things were called in the spec, and did a console.log() to show that component.observableResult$ never emits.

Update

As per the comments below, the Stackblitz link above has been updated and is now working. From that Stackblitz here is the working spec:

it('should change return of service.function1() only', () => {
    fixture.detectChanges(); 
    component.observableResult$.subscribe(value => {
        console.log('observable Emitted, value is ', value);
        expect(value[0].id==1).toBe(true); 
    });
    component.myForm.get('localId').setValue(1);
});

The key to this was to set up the subscribe first, and then emit a new value in the form, which will update the the combineLatest() and execute the code inside the subscribe().

I am glad this worked!

Community
  • 1
  • 1
dmcgrandle
  • 5,934
  • 1
  • 19
  • 38
  • Thanks for your effort!!! I have tried your code ,while debugging ,before expect i am getting subscribed value as undefined. – Mithun S Dec 03 '18 at 12:22
  • Yes, that is what I meant above in the discussion about the observable being hard to get a hold of during testing the way you have it coded. It probably needs to be refactored, but only you know what this code is actually trying to accomplish - it is not clear to me from what you have posted so far. – dmcgrandle Dec 03 '18 at 16:23
  • I was trying to properly chain the observables using combineLatest(), i have 2 controls One for Country other one for States, when the country changes in first control(first argument of combineLatest), it should trigger combineLatest and fetch the corresponding states from country service(2nd argument of combineLatest). The second argument result conatins Array of object then i have to pipe it and filter states using selected country(using selected country- the first paramter of combineLatest) – Mithun S Dec 04 '18 at 06:56
  • I have properly implemented this, but not getting test case coverage. – Mithun S Dec 04 '18 at 07:01
  • Do you have it in a Stackblitz so I can take a look? – dmcgrandle Dec 04 '18 at 07:06
  • 1
    https://stackblitz.com/edit/stackoverflow-q-53538362-ebvdtj This is almost same as the link you provided, not able to reach the expect block inside test case – Mithun S Dec 04 '18 at 07:15
  • 1
    Got it working by simply moving `component.myForm.get('localId').setValue(1);` to after the subscribe. Need to think asynchronously - once the subscribe has been set up, THEN we trigger a new emission with `setValue(1)`, which will cause the subscribe to then be executed. :) – dmcgrandle Dec 04 '18 at 08:06
  • Thanks a lot @dmcgrandle, finally it worked!! Can you update the same in Stackblitz and post as an answer so that i can accept it, then it will be very catchy for others too!! – Mithun S Dec 04 '18 at 08:49
  • I will do, tomorrow. Glad we got it sorted. :) – dmcgrandle Dec 04 '18 at 09:07