如何为调用服务的功能编写单元和/或e2e测试

时间:2019-11-19 17:24:57

标签: angular unit-testing e2e-testing angular-e2e angular-unit-test

我将如何为此功能编写单元测试?提交表单后,它将发送POST请求以获取登录令牌。通过令牌响应,它随后发送另一个请求以获取特定的用户详细信息。

功能如下:

// Log the user in and save the token, then get the details
onLoginSubmit() {
    this.loggingIn = true;
    // request the token then the details for the user based off the token
    this.userService
        .LoginUser(this.loginForm.value.username, this.loginForm.value.password)
        .pipe(
            tap(res => {
                // console.log('Obtained Token:', res);
                localStorage.setItem('login_token', res.token);
                // this.utilityService.SetLocalStorage('login_token', res.token, 1, 'd');
            }),
            concatMap(() => this.userService.GetTokenDetails()),
        )
        .subscribe(
            res => {
                // console.log('Obtained User Data:', res);
                localStorage.setItem('user', res.user);
                // this.utilityService.SetLocalStorage('user', res.user, 1, 'd');
                this.NavigateToRoute();
            },
            () => {
                this.loggingIn = false;
            },
        );
}

以下是服务功能:

// logs in the user
LoginUser(email, password): Observable<any> {
    return this.utilityservice.post('universal/login', { email, password }, this.utilityservice.GetHeaders());
}

// gets details of the current logged in user
GetTokenDetails() {
    return this.utilityservice.get('universal/userAPI', { whoami: '' });
}

对于单元测试,我假设我需要创建一个模拟服务,其中这些函数将返回如下所示的有效响应:

// logs in the user
LoginUser(email, password): Observable<any> {
    return { res: { token: 'test' } };
}

这是正确的方法还是我完全错误?同样,对于登录页面的E2E测试,我本质上是在每次测试中通过编写代码来模拟用户,该代码模拟单击表单按钮并输入值,然后检查以确保获得有效的响应,或者我是否使其复杂化? >

2 个答案:

答案 0 :(得分:0)

大多数测试库(如Jasmine或Mocha)将包含模拟工具,以帮助您编写这些测试。否则,您将走在正确的轨道上。

您的单元测试不应测试服务,而只能测试被测试的代码,在这种情况下,该代码似乎是组件。

我个人在规范中使用RxJs库-因此我可以创建一个类似的模拟类,并提供一个实例:

class MockUserService {
  public loginSubject = new Subject();
  public loginCalls = [];
  // logs in the user
  public LoginUser(email, password): Observable<any> {
    this.loginCalls.push({ email, password });
    return this.loginSubject.asObservable();
  }
}

这样,在我的规格书中的其他地方,我可以做这样的事情:

it('calls login on the user service', () => {
  expect(mockUserService.loginCalls.length > 0).toBe(true);
});

it('sets value on local storage', fakeAsync(() => {
  mockUserService.loginSubject.next({ res: { token: 'test-token' }});
  flushMicrotasks();
  expect(mockLocalStorage.setItem).toHaveBeenCalledWith('login_token', 'test-token', 1, 'd');
}));

通常使用Jasmine编写规范,因此您实际上可以像这样进行设置(此处的示例规范使用jasmine的结构)。

loginSubject = new Subject();
const mockUserService = jasmine.createSpyObj('UserService', ['LoginUser']);
mockUserService.LoginUser.and.returnValue(loginSubject.asObservable());

要进行更多设置才能使其正常运行;依赖注入是很棒的,因为它使您能够编写规范,但是它也增加了一些样板。我通常在顶部配置测试,如下所示:

describe('LoginComponent', () => {
  let loginSubject: Subject<any>;
  let mockLocalStorage: jasmine.SpyObj<LocalStorageService>;
  let fixture: ComponentInstance<LoginComponent>;

  beforeEach(() => {
    loginSubject = new Subject();
    const mockUserService = jasmine.createSpyObj('UserService', ['LoginUser']);
    mockUserService.LoginUser.and.returnValue(loginSubject.asObservable());

    mockLocalStorage = jasmine.createSpyObj('LocalStorage', ['setItem']);

    TestBed.configureTestingModule({
      declarations: [LoginComponent],
      providers: [
        { provide: UserService, useValue: mockUserService },
        { provide: LocalStorageService, useValue: mockLocalStorage }
      ]
    });

    fixture = TestBed.createComponent(LoginComponent);
  });

  it('calls login on the user service', () => {
    expect(mockUserService.loginCalls.length > 0).toBe(true);
  });

  it('sets value on local storage', fakeAsync(() => {
    loginSubject.next({ res: { token: 'test-token' }});
    flushMicrotasks();
    expect(mockLocalStorage.setItem).toHaveBeenCalledWith('login_token', 'test-token', 1, 'd');
  }));
});

我正在做大量假设(例如您已经以某种方式内联了模板,否则在创建组件时需要异步beforeEach)。

最后,您可能应该在这里查看手册:https://angular.io/guide/testing

答案 1 :(得分:0)

您问了2个问题;关于端到端测试。假设您必须登录,则登录页面会针对其他所有规格进行测试。我的配置是使用我的注册页面创建一个新用户,注销并使用这些凭据登录。之后,我继续进行其他测试。这样一来,我避免为测试设置很多罐装数据。

确实,对于e2e更改而言,重要的是根据应用程序的需求。我会挑选一些您最重要的页面,并确保它们被e2e覆盖,因为e2e规范1.)构建成本高昂,并且2.)可能存在问题。不过,登录页面几乎始终是“对于应用程序最重要的部分”的一部分。