1

我将如何为这个函数编写单元测试?提交表单时,它会发送一个 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 测试时,我基本上是通过编写模拟单击表单按钮和输入值的代码来模拟每个测试中的用户,然后检查以确保我得到有效的响应,或者我是否过于复杂了?

4

2 回答 2

0

你问了 2 个问题;关于端到端测试。假设您必须登录,登录页面针对所有其他规范进行了测试。我的配置是使用我的注册页面创建一个新用户,注销然后使用这些凭据登录。之后,我继续进行其他测试。这就是我避免为测试设置大量预设数据的方法。

确实,对 e2e 重要的是根据应用程序的需求而变化。我会挑选一些您最重要的页面,并确保它们被 e2e 覆盖,因为 e2e 规范是 1.) 构建成本高 2.) 可能不稳定。但是,登录页面几乎总是“对应用程序最重要的内容”的一部分,如果它存在的话。

于 2019-11-19T18:49:05.363 回答
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

于 2019-11-19T18:11:32.500 回答