Как протестировать сервис Angular 6 с зависимостью, которая использует частные методы / свойства для изменения вывода открытых методов / свойств - PullRequest
1 голос
/ 30 октября 2019

Я застрял при попытке написать тест Jasmine / Karma в приложении Angular 6 для сервиса, который зависит от другого сервиса, и эта зависимость имеет несколько закрытых свойств / методов, которые изменяют выполнение публичного метода, и мой тествсегда терпит неудачу.

Весь код работает должным образом во время выполнения, но я не понимаю, как это правильно проверить. Я пробовал шпионов для закрытых методов и свойств, которые не работают и не меняют их на публичные. Очевидно, я что-то упускаю.

Сейчас мне все равно или я хочу проверить DependencyService. Я хочу иметь возможность проверить, работает ли MyService функция doSomething, в настоящее время она всегда возвращает null, поэтому мой результат теста равен Failed: Cannot read property 'subscribe' of null. Удаление оператора if в postData() возвращает наблюдаемое, как и ожидалось, и тест проходит успешно.

Мне кажется, что я шпионю за неправильными вещами, поскольку мой тест тесно связан со службой зависимостей или localStorage значений.

Как мне издеваться / шпионить за checkAuth и isAuth в моей служебной зависимости? Возможно, точнее, как правильно проверить doSomething(), чтобы тест был изолирован для службы MyService?

export class MyService {
    constructor(private depService: DependencyService) { }

    public doSomething(additionalPayload: Object) {
        const payload = { ...additionalPayload, modified: true };
        return this.depService.postData('/api/endpoint', payload);
    }

}

export class DependencyService {
    constructor(private httpClient: HttpClient) { }

    private isAuth: boolean = false;

    private checkAuth() {
        const token = localStorage.get('token');
        if (token !== null) {
            this.isAuth = true;
        } else {
            this.isAuth = false;
        }
    }

    postData(url, body): Observable<any> {
        this.checkAuth();
        if (!this.isAuth) {
            return null;
        }
        return this.httpClient.post(url, body);
    }
}

Пока что myservice.spec.ts не проходит:

describe('MyService', () => {
  let httpTestingController: HttpTestingController;

  let myService: MyService;
  let dependencyServiceSpy: jasmine.SpyObj<DependencyService>;

  beforeEach(() => {
    const dependencyServiceSpyObj = jasmine.createSpyObj('DependencyService', ['postData']);

    TestBed.configureTestingModule({
      imports: [ HttpClientTestingModule ],
      providers: [
        MyService,
        { provide: DependencyService, useValue: dependencyServiceSpyObj },
      ]
    });

    httpTestingController = TestBed.get(HttpTestingController);

    myService = TestBed.get(MyService);
    dependencyServiceSpy = TestBed.get(DependencyService);
  });

  afterEach(() => {
    httpTestingController.verify();
  });

  it('#doSomething should post some data', async(() => {
    const payloadData: Object = {
      name: 'Ash',
      food: 'donut'
    };

    const responseData: Object = {
      success: true,
      msg: 'Payload received'
    };

    // HELP HERE ↓
    // need to spy/mock dependencyService.isAuth so that it is `true`
    // otherwise inside `postData` the if statement will always return a `null` value
    // ...spy/mock `localStorage`?
    dependencyServiceSpy.postData.and.returnValue(/* return http observable so that .subscribe can be called */);

    myService.doSomething(payloadData).subscribe(data => {
      expect(data).toEqual(responseData);
    }, fail);

    const req = httpTestingController.expectOne('/api/endpoint');

    expect(req.request.method).toEqual('POST');
    expect(req.request.body).toEqual({ ...payloadData, modified: true });

    expect(dependencyServiceSpy.postData.calls.count()).toBe(1);
    expect(dependencyServiceSpy.postData.calls.mostRecent().returnValue).toBe(responseData);

    req.flush(responseData);
  }));
});

1 Ответ

2 голосов
/ 30 октября 2019

Вам не нужно беспокоиться о службе зависимостей. Если пользователь аутентифицирован или сетевые вызовы выполняются правильно или нет, это должно быть частью спецификации для службы зависимостей.

Существует также проблема с тем, как вы шпионите dependencyService. TestBed.get(DependencyService) возвращает текущий экземпляр DependencyService, а не шпион. Было бы разумно переименовать переменную, как показано ниже:

let dependencyService: DependencyService;

и присвоение, как показано ниже:

dependencyService = TestBed.get(DependencyService);

Вам просто нужно шпионить postData метод.

С точки зрения MyService, есть только два сценария для DependencyService.

  1. Пользователь не аутентифицирован

    В этом случаевсе, что вам нужно, это то, что postData должен вернуть ноль. Вам не нужно беспокоиться о checkAuth. Вы можете шпионить за postData и возвращать Observable с нулевым значением. Вы беспокоитесь только о выходе метода postData, а не о том, как он генерируется.

    it('#doSomething should return null if user is not authenticated', () => {
        const payloadData = {
            name: 'Ash',
            food: 'donut'
        };
    
        spyOn(dependencyService, 'postData').and.returnValue(Observable.create(observer => {
            observer.next(null);
            observer.complete();
        }));
    
        myService.doSomething('/api/endpoint', payloadData).subscribe(data => {
            expect(data).toBeNull();
        }, fail);
    
    });
    

    Как вы можете видеть выше, вам не нужно указывать, как postData получает возвращаемый ноль. Вы знаете, что в этом случае postData должно возвращать ноль. Как это достигается, необходимо проверить в спецификации DependencyService.

  2. Пользователь аутентифицирован

    В этом случае postData возвращаетзначение из HTTP-вызова. Опять же, вам просто нужно вернуть значение. Если сетевой вызов сделан правильно или нет, это должно быть проверено в спецификации DependencyService.

    it('#doSomething should post some data', () => {
        const payloadData = {
            name: 'Ash',
            food: 'donut'
        };
    
        const responseData = {
            success: true,
            msg: 'Payload received'
        };
    
        spyOn(dependencyService, 'postData').and.returnValue(Observable.create(observer => {
            observer.next(responseData);
            observer.complete();
        }));
    
        myService.doSomething('/api/endpoint', payloadData).subscribe(data => {
            expect(data).toEqual(responseData);
        }, fail);
    
    });
    
...