简体   繁体   中英

Jasmine using marbles to test multiple observable values

So I am trying to test the HTML based on if an observable emits certain values. I have the inital setup of the service to have an observable emit the correct value but when I go to create another test to test if what happens if I pass wrong data I am unable to change the value the observable emits. I feel like its something small that I am missing could someone take a look and let me know what I am doing wrong?

Here is the spec file

describe('AlertsComponent', () => {
  let component: AlertsComponent;
  let fixture: ComponentFixture<AlertsComponent>;
  let alertService: any;

  let testAlertGood: Alert = {
    type: AlertType.Success,
    title: 'Test title',
    message: 'Test message',
    forceAction: false
  };

  let testAlertBad: String = 'bad alert';

  let testAlertNoTitle: Alert = {
    type: AlertType.Success,
    title: null,
    message: 'Test message',
    forceAction: false
  };

  beforeEach(async(() => {
    alertService = jasmine.createSpy('AlertService');
    alertService.alert$ = cold('a', { a: testAlertGood });

    TestBed.configureTestingModule({
      declarations: [ AlertsComponent ],
      schemas: [ NO_ERRORS_SCHEMA ],
      providers: [
        {
          provide: Router,
          useClass: class { navigate = jasmine.createSpy('navigate'); }
        },
        {
          provide: AlertService,
          useValue: alertService
        }
      ]
    })
    .compileComponents();
  }));

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

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

  it('should display an alert if the alert$ observable has an Alert value', async () => {
    fixture.detectChanges();
    getTestScheduler().flush();
    fixture.detectChanges();

    const alertElements = fixture.debugElement.queryAll(By.css('.alert-container'));
    const alertIconContainer = fixture.debugElement.query(By.css('.icon-container'));
    const alertIconClass = fixture.debugElement.query(By.css('#alert-icon'));
    const alertTitle = fixture.debugElement.query(By.css('.title'));
    const alertBody = fixture.debugElement.query(By.css('.body'));

    expect(alertElements.length).toBe(1);
    expect(alertIconContainer.nativeElement.getAttribute('class')).toContain('bg-success');
    expect(alertIconClass.nativeElement.getAttribute('class')).toContain('fa-check-circle');
    expect(alertTitle.nativeElement.innerText).toContain('Test title');
    expect(alertBody.nativeElement.innerText).toContain('Test message');
  });

  it('should hide the title p tag if the Alert.title is null', async () => {
    alertService.alert$ = cold('a', { a: testAlertNoTitle });

    fixture.detectChanges();
    getTestScheduler().flush();
    fixture.detectChanges();

    const alertTitle = fixture.debugElement.query(By.css('.title'));
    expect(alertTitle).toBeNull();
  });
});

So basically at the top of the files I have the three versions of values that I need to test when the observable emits and I am only able to test the first one. the should display an alert if the alert$ test passes just fine but its the last one should hide the title... that is failing because it does not seem to be changing the observable when I do alertService.alert$ = cold('a', { a: testAlertNoTitle });

You don't need jasmine-marbles here. The package is useful when testing the interaction of multiple observables and you clearly have just one. To me marbles look like overkill here.

The problem in the last it() is that you replace the value of alertService.alert$ with another observable after the component had subscribed to the initial value. Here's what happens.

  1. In beforeEach the spy service is created and cold('a', { a: testAlertGood }) is assigned to alert$.
  2. In beforeEach the component is created. I believe it subscribes to alert$ in ngOnInit() or via the async pipe.
  3. The component gets testAlertGood from the observable.
  4. it() starts and assigns cold('a', { a: testAlertNoTitle }) to alert$. It doesn't change a thing, because 2 and 3 have already happened.

I'd suggest using a good old Subject instead of marbles here. So you don't need to change the observable, but you change its emitted value. Something like this:

describe('AlertsComponent', () => {
    let component: AlertsComponent;
    let fixture: ComponentFixture<AlertsComponent>;
    let alertService: any;

    let testAlertGood: Alert = {
        type: AlertType.Success,
        title: 'Test title',
        message: 'Test message',
        forceAction: false
    };

    let testAlertBad: String = 'bad alert';

    let testAlertNoTitle: Alert = {
        type: AlertType.Success,
        title: null,
        message: 'Test message',
        forceAction: false
    };
    
    const alertSubj = new Subject<any>();

    beforeEach(async(() => {
        alertService = jasmine.createSpy('AlertService');
        alertService.alert$ = alertSubj.asObservable();

        TestBed.configureTestingModule({
            declarations: [ AlertsComponent ],
            schemas: [ NO_ERRORS_SCHEMA ],
            providers: [
                {
                    provide: Router,
                    useClass: class { navigate = jasmine.createSpy('navigate'); }
                },
                {
                    provide: AlertService,
                    useValue: alertService
                }
            ]
        })
            .compileComponents();
    }));

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

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

    it('should display an alert if the alert$ observable has an Alert value', async () => {
        alertSubj.next(testAlertGood);
        
        fixture.detectChanges();
        getTestScheduler().flush();
        fixture.detectChanges();

        const alertElements = fixture.debugElement.queryAll(By.css('.alert-container'));
        const alertIconContainer = fixture.debugElement.query(By.css('.icon-container'));
        const alertIconClass = fixture.debugElement.query(By.css('#alert-icon'));
        const alertTitle = fixture.debugElement.query(By.css('.title'));
        const alertBody = fixture.debugElement.query(By.css('.body'));

        expect(alertElements.length).toBe(1);
        expect(alertIconContainer.nativeElement.getAttribute('class')).toContain('bg-success');
        expect(alertIconClass.nativeElement.getAttribute('class')).toContain('fa-check-circle');
        expect(alertTitle.nativeElement.innerText).toContain('Test title');
        expect(alertBody.nativeElement.innerText).toContain('Test message');
    });

    it('should hide the title p tag if the Alert.title is null', async () => {
        alertSubj.next(testAlertNoTitle);

        fixture.detectChanges();
        getTestScheduler().flush();
        fixture.detectChanges();

        const alertTitle = fixture.debugElement.query(By.css('.title'));
        expect(alertTitle).toBeNull();
    });
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM