如何编写针对角度5的api失败的单元测试用例?

时间:2018-10-24 03:05:42

标签: angular unit-testing

嗨,我正在为Angular 5应用程序编写单元测试用例。我在我的项目中使用了api。我正在为肯定和否定场景编写单元测试用例。我已经完成了积极的案例。我正在努力编写负面的测试案例。例如我有ngoninit方法将数据加载到网格。下面是我的代码段。

 ngOnInit() {
        this.loadTenantsData();
        this.columns = [
            { prop: "index", name: '#', width: 30, headerTemplate: this.statusHeaderTemplate, cellTemplate: this.statusTemplate, resizeable: false, canAutoResize: false, sortable: false, draggable: false },
            { prop: 'tenantname', name: 'Tenant', cellTemplate: this.nameTemplate, width: 200 },
            { name: '', width: 80, cellTemplate: this.actionsTemplate, resizeable: false, canAutoResize: false, sortable: false, draggable: false }
        ];
    }

下面是loadTenantsData方法。

 private loadTenantsData() {
        this.tenantService.getTenants().subscribe(results => this.onTenantDataLoadSuccessful(results), error => this.onTenantDataLoadFailed(error));
    }

以下是我的租户服务。

 getTenants(page?: number, pageSize?: number) {
        return this.tenantEndpoint.getTenantsEndpoint<Tenant[]>(page, pageSize);
    }

以下是我的租户终结点服务。

   getTenantsEndpoint<T>(page?: number, pageSize?: number): Observable<T> {
        return Observable.create(observer => {
            var tenants = [{
                'tenantid': 'bcdaedf3-fb94-45c7-b6a5-026ca4c53233',
                'tenantname': 'BENZAAD.onmicrosoft.com'
            }
            ];
            if (!tenants) {
                throw 'no tenants given'
            }
            observer.next(tenants);
        });
    }

下面是我的错误处理程序。

 private onTenantDataLoadFailed(error: any) {
        if (typeof error.error.title != 'undefined') {
            this.alertService.stopLoadingMessage();
            this.alertService.showStickyMessage("Load Error", `Unable to retrieve tenant data from the server.\r\nErrors: "${Utilities.getHttpResponseMessage(error)}"`,
                MessageSeverity.error, error);
            this.rows = [];
            this.loadingIndicator = false;
            this.alertService.showMessage(error.error.title, error.error.status, MessageSeverity.error);
        }
    }

我已将所有单元测试用例文件放在下面。

describe('Component: TenantEditorComponent', () => {

let component: TenantEditorComponent;
let fixture: ComponentFixture<TenantEditorComponent>;
let submitEl: DebugElement;
let el: HTMLElement;
let scopename: DebugElement;
let scopeObject;

const mockResults = { /* whatever your results should look like ... */ };
const spyTenantService = jasmine.createSpyObj({ getTenants: of(mockResults), });
const spyAlertService = jasmine.createSpyObj({
    stopLoadingMessage: null,
    showStickyMessage: null,
    showMessage: null
});

beforeEach(async(() => {
    TestBed.configureTestingModule({
        imports: [
            BrowserAnimationsModule,
            HttpClientModule,
            RouterTestingModule,
            TranslateModule.forRoot({
                loader: {
                    provide: TranslateLoader,
                    useClass: TranslateLanguageLoader
                }
            }),
            NgxDatatableModule,
            FormsModule,
            UiSwitchModule,
            TooltipModule.forRoot(),
            ModalModule.forRoot(),
            SimpleNotificationsModule.forRoot(),
            HttpClientTestingModule
        ],
        declarations: [
            TenantEditorComponent,
            SearchBoxComponent
        ],
        providers: [
            {
                provide: LogMessages, useClass: LogMessagesMock
            },
            HtmlEncoding,
            {
                provide: Adal5Service, useClass: MockAdal5Service
            },
            TenantService,
            UnitTestStorageOperations, TenantEndpoint,
            TenantsEndpointMock,
            AlertService,
            AppContextService,
            EndpointFactory,
            NotificationsService,
            AppTranslationService,
            ConfigurationService,
            LocalStoreManager,
            {
                provide: TenantEndpoint, useClass: TenantsEndpointMock
            },
            { provide: TenantService, useValue: spyTenantService }
        ]
    }).compileComponents();
}));

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

it('ngOnInit should call onTenantDataLoadFailed() in case of error', () => {
    var error = {
        error: {
            title: 'Tenant already exists',
            status: '409'
        }
    }
    spyOn(component, 'onTenantDataLoadFailed').and.callThrough();
    debugger;
    spyTenantService.getTenants.and.returnValue(ErrorObservable.create({ error }));
    fixture.detectChanges();
    expect(spyTenantService.getTenants).toHaveBeenCalledTimes(1);
    expect(spyAlertService.stopLoadingMessage).toHaveBeenCalled();
    expect(component.onTenantDataLoadFailed).toHaveBeenCalled();
    expect(spyAlertService.showStickyMessage).toHaveBeenCalled();
    expect(spyAlertService.showMessage).toHaveBeenCalled();
});

例如,出于任何原因,API均可归档。在这种情况下,我的错误处理程序将被调用。我想为这种情况编写单元测试用例。有人可以帮我编写负面案例的单元测试用例吗?任何帮助,将不胜感激。谢谢。

2 个答案:

答案 0 :(得分:2)

可以在成功(肯定)情况之后立即测试失败(负面)情况。有很多方法可以完成此操作,而到目前为止您还没有提供测试(spec)文件的外观。我将从为服务创建间谍开始,然后将它们添加到TestBed的提供程序中。

另一个关键是在设置成功或失败的spy.returnValue()之前,不要调用夹具.detectChanges(),因此请在it()规范定义中进行此操作。

类似这样的东西:

import { of, throwError } from 'rxjs';

const mockResults = { /* whatever your results should look like ... */ };
const spyTenantService = jasmine.createSpyObj({getTenants: of(mockResults),});
const spyAlertService = jasmine.createSpyObj({
  stopLoadingMessage: null,
  showStickyMessage: null,
  showMessage: null
});
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;

beforeEach(async(() => {
    TestBed.configureTestingModule({
        declarations: [MyComponent],
        imports: [ ],
        providers: [
            { provide: TenantService, useValue: spyTenantService },
            { provide: AlertService, useValue: spyAlertService },
        ]
    })
    .compileComponents();
}));

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

it('ngOnInit should initialize properly in positive (no error) case', () => {
    fixture.detectChanges();
    expect(component.results).toEqual(mockResults); // Something like this ...
    /* I have no idea what this should look like - you said you have it working */
});
it('ngOnInit should call onTenantDataLoadFailed() in case of error', () => {
  spyOn(component, 'onTenantDataLoadFailed').and.callThrough();
  spyTenantService.getTenants.and.returnValue(throwError({error: {title: 'defined'}}));
  fixture.detectChanges();
  expect(spyTenantService.getTenants).toHaveBeenCalledTimes(1);
  expect(spyAlertService.stopLoadingMessage).toHaveBeenCalled();
  expect(component.onTenantDataLoadFailed).toHaveBeenCalled();
  expect(spyAlertService.showStickyMessage).toHaveBeenCalled();
  expect(spyAlertService.showMessage).toHaveBeenCalled();
});

更新

感谢您提供规格文件。正如我在上面的评论中提到的那样,您应该删除您在provider数组中为TenantService和AlertService提供的现有条目,因为您将通过间谍而不是原始服务来提供这些条目。您还忘记了添加以下行:

{ provide: AlertService, useValue: spyAlertService }

因此,提供的提供程序数组应如下所示:

providers: [
    { provide: LogMessages, useClass: LogMessagesMock },
    HtmlEncoding,
    { provide: Adal5Service, useClass: MockAdal5Service },
    UnitTestStorageOperations,
    AppContextService,
    EndpointFactory,
    NotificationsService,
    AppTranslationService,
    ConfigurationService,
    LocalStoreManager,
    { provide: TenantEndpoint, useClass: TenantsEndpointMock },
    { provide: TenantService, useValue: spyTenantService },
    { provide: AlertService, useValue: spyAlertService }
]

注意:我还从阵列中的较早位置删除了TenantEndpoint和TenantsEndpointMock,因为稍后再指定-您只希望为要注入组件的每个提供程序输入一个条目。但是...我没有看到TenantsEndpointMock的定义,所以我猜想它是在您上面粘贴的代码片段之前声明的,否则也会给您带来错误。 :)

答案 1 :(得分:1)

我认为这应该可以为您提供帮助,如果您有任何问题,请告诉我:

import { async, ComponentFixture, TestBed, fakeAsync, flush } from '@angular/core/testing';
import { ChatPageComponent } from './chat-page.component';
import { FormsModule } from '@angular/forms';
import { UserService } from '../user.service';
import { UserMockService } from '../test/user-mock.service';
import { HubService } from '../hub.service';
import { HubMockService } from '../test/hub-mock.service';
import { CommonHttpService } from '@matrixtools/common';
import { HttpMockService } from '../test/http-mock.service';

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ChatPageComponent],
      providers: [{ provide: UserService, useClass: UserMockService },
        { provide: HubService, useClass: HubMockService },
        { provide: CommonHttpService, useClass: HttpMockService }],
      imports: [FormsModule]
    })
    .compileComponents();
  }));

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

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

  describe('ping function', () => {
    it('should append a green message if hub connects', fakeAsync(() => {
      // arrange
      spyOn(component.hubConnection, 'invoke').and.returnValue(Promise.resolve('good promise'));
      let expectedColor = 'green';
      component.messages = [];

      // act
      component.ping();
      flush();

      // assert
      let actualColor = component.messages[1].color;
      expect(actualColor).toBe(expectedColor);
    }));

    it('should append a red message if hub connection fails', fakeAsync(() => {
      // arrange
      spyOn(component.hubConnection, 'invoke').and.returnValue(Promise.reject('bad promise'));
      let expectedColor = 'red';
      component.messages = [];

      // act
      component.ping();
      flush();

      // assert
      let actualColor = component.messages[1].color;
      expect(actualColor).toBe(expectedColor);
    }));
  });
});