Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit test 'success' and 'error' observable response in component for Angular 2

I'm writing a unit test for a component that makes a call to a service OnInit. If the response is a 'success' one action taken and another for an 'error'.

What is the best way to test both cases? I've created a simplified version of the component and unit test. Something that I could easily test against in both cases.

I've attempted to implement the solution here but I must be off on the implementation. I've also attempted to throw an error as you will see in the spec and comments.

Component

@Component({
  selector: 'app-observer-throw-unit-test',
  template: '<p>{{ data }}</p>'
})
export class ObserverThrowUnitTestComponent implements OnInit {
    public data: string;

    constructor(private _observerThrowService: ObserverThrowService) { }

    ngOnInit() {
        this._observerThrowService.getData().subscribe(
            (data) => {
                this.data = data;
            },
            (error) => {
                this.redirect()
            }
        )
    }

    redirect() {
        this.data = "Redirecting...";
    }

}

Spec

const data: string = "Lorem ipsum dolor sit amet.";

const ObserverThrowServiceStub = {
  error: false,
  getData() {
    return Observable.create((observer) => {
      if(this.error) {
        observer.error(new Error());
      } else {
        observer.next(data);
      }
      observer.complete();
    })
  }
}

describe('ObserverThrowUnitTestComponent', () => {
  let component: ObserverThrowUnitTestComponent;
  let fixture: ComponentFixture<ObserverThrowUnitTestComponent>;
  let _observerThrowService: ObserverThrowService;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ ObserverThrowUnitTestComponent ],
      providers: [
        { provide: ObserverThrowService, useValue: ObserverThrowServiceStub },
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(ObserverThrowUnitTestComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
    _observerThrowService = TestBed.get(ObserverThrowService);
  });

  it('should set "data" to "Lorem ipsum dolor sit amet." on success', () => {
      expect(component.data).toEqual("Lorem ipsum dolor sit amet.");
  });

  it('should set "data" on "Redirecting..." on error',() => {
    ObserverThrowServiceStub.error = true;
    // spyOn(_observerThrowService, "getData").and.returnValue(Observable.throw("error")); // This did not work and returned : TypeError: undefined is not a constructor (evaluating 'Observable_1.Observable.throw("error")') in src/test.ts
    spyOn(_observerThrowService, "getData")
    expect(component.data).toEqual("Redirecting...");
  });

  it('should set "data" on "Redirecting..." on error',() => {
    // This works after setting error to true in the previous test
    spyOn(_observerThrowService, "getData")
    expect(component.data).toEqual("Redirecting...");
  });

});
like image 215
bmd Avatar asked Dec 03 '22 21:12

bmd


1 Answers

I would create two mocks - one that throws an error:

class ObserverThrowServiceStub {
    getData() {
        return Observable.throw(new Error('Test error'));
    }
}

and one that returns successfully.

class ObserverSuccessServiceStub {
    getData() {
        return Observable.from(data);
    }
}

Then, instead of providing the same mock service to all tests, you provide the failing/successful mock service appropriately depending on the test in question (obviously you'll need to move your module configuration into a configurable method that you call from within each test rather than in the .beforeEach() code.

There is a really nice article on testing Angular services using Observables here (which uses exactly this model): http://www.zackarychapple.guru/angular2/2016/11/25/angular2-testing-services.html

like image 50
Steve Land Avatar answered Dec 29 '22 08:12

Steve Land