3

I am unit-testing a component which renders a list of codes from server.

Response coming is of this format:

{
  ContactActivityView :[
        {
           Code:"AB",
           IsActive:false,
        },
        {
           Code:"BC",
           IsActive:true,
        }
        ..
        ...
  ]
}

In my component.ts file I consume it in the following way:

codesArray: ICodeModel[];

ngOnInit() {
  this.getCodes();
}

getCodes() {
    this.service.getCodes()
      .subscribe((response: ICodeModel[] | []) => {
        this.codesArray = response['ContactActivityView'];
      },
      error => console.log(error)
    );
}

It works fine and I am able to show my data using component.html file:

  ..
  ...
    <div  class="message" *ngIf="codesArray.length === 0">
      No Data Found.
    </div>
    <div class="tbl-row" *ngFor="let code of codesArray">
      <div class="item">
        {{ code.Code }}
      </div>
      <div class="item">
        {{ code.IsActive }}
      </div>
  ..
  ...

component.service file:

..
...
getCodes(): Observable<ICodeModel[]> {
  return this._service.get(this.codeURL);
}
..
...

Now when I run my component.spec file it gives me an error:

TypeError: Cannot read property 'length' of undefined

I realized that in .html my codesArray is undefined due to which length is undefined.

I console my codesArray. It is showing data in comp.ts file but comes undefined in comp.spec file

Not sure what am I doing wrong as I am able to render the data properly, but somehow my test is failing. Here's my component.spec file

..
...
class MockCodesService {
  getCodes(): Observable<ICodeModel[]> {
    return of([]);
  }

  addCode(param: ICodeModel){
    return of({});
  }

  updateCode(param: ICodeModel) {
    return of({});
  }
}

describe('CodesComponent', () => {
  let component: CodesComponent;
  let fixture: ComponentFixture<CodesComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        CodesComponent
      ],
      providers: [
        { provide: CommonCrudService, useClass: CommonCrudMockService },
        { provide: MessageService, useClass: MessageMockService },
        { provide: AppService, useClass: AppMockService },
        { provide: CodesService, useClass: MockCodesService }
      ],
      schemas: [NO_ERRORS_SCHEMA]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(CodesComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

Thanks.

Mr. A
  • 63
  • 1
  • 14

1 Answers1

0

In MockCodesService getCodes() is returning an observable from an array but in your component this.service.getCodes() expects an observable from an object with a ContactActivityView property. Try doing something like this in MockCodesService:

getCodes(): Observable<ICodeModel[]> {
  return of({
    ContactActivityView :[
    {
       Code:"AB",
       IsActive:false,
    },
    {
       Code:"BC",
       IsActive:true,
    }]
  });
}
alexortizl
  • 2,125
  • 1
  • 12
  • 27
  • Thanks for the response. Now I understand. But then I have around 200-300 code objects in my data. So is it a good practice to specify my real data inside a Mock Service? Because I feel then I have to call the real method of my service. Is it possible to use only skeleton of my real data in my mock service? Please enlighten on this. Thanks. – Mr. A Apr 20 '20 at 06:54
  • @Mr.A glad to help. Usually it's not a good practice to test your components using real services. The point of a Unit Test is to test your component isolated from everything else, including services. Using a mock you can control what your component gets without worrying about the service implementation. If you need to test how your component behaves with 200+ objects maybe you can make a function that generates 200+ dummy datas in your mock service. Also if your service make calls to a server using the real one may slow your tests. – alexortizl Apr 20 '20 at 07:00
  • Now I am trying with a single object as : return of( { ContactActivityView : [testObject] } ); testObject is one of the code info objects – Mr. A Apr 20 '20 at 08:20
  • But this one doesn't help :( – Mr. A Apr 20 '20 at 08:21