جاوااسکریپت - کدام روش به عنوان روش صحیح برای تست متدهایی که observables HTTP بازمیگردانند در نظر گرفته میشود؟


من یک پروژه تایپاسکریپت دارم که میخواهم به عنوان بسته NPM جاوااسکریپت منتشر کنم. این بسته برخی درخواستهای HTTP را با استفاده از توابع ajax در rxjs انجام میدهد. حالا میخواهم برای این متدها تست بنویسم.

در مقطعی من روشی مثل این دارم (سادهشده!):

getAllUsers(): Observable<AjaxResponse> {
    return ajax.get(this.apiUrl + '/users');
}

من با تستهای پایه آشنا هستم، برای مثال با spyOn میتوانم یک پاسخ از سرور شبیهسازی کنم. اما چگونه میتوانم خود درخواست http را تست کنم؟

مستندات جاسمین میگوید که نمیتوانم کار غیرهمزمان را در بخش it انجام دهم، اما میتوانم در beforeEach انجام دهم: https://jasmine.github.io/tutorials/async

آیا این روش صحیح برای تست API خواهد بود؟

let value: AjaxResponse;
let error: AjaxError;

beforeEach((done) => {

    const user = new UsersApi();
    user.getAllUsers().subscribe(
        (_value: any) => {
            value = _value;
            done();
        },
        (_error: any) => {
            error = _error;
            done();
        }
    );

});

it("should test the actual http request", () => {

    // Test here something
    // expect(value).toBe...
    // expect(error).toBe...

});

من نتوانستم راه دیگری برای انجام کار غیرهمزمان بیابم...

پاسخ شماره ۱

شما نیاز دارید ajax.get را شبیهسازی کنید تا یک Observable برگرداند که مقادیری که میخواهید آزمایش کنید را انتشار میدهد.

این بستگی به این دارد که ajax چگونه در فایل شما که شامل متد user.getAllUsers است، اعلام شده است.

ایدهآل است اگر UsersApi() شامل ajax باشد که به آن منتقل شده است (به سبک تابع خالص) زیرا در این صورت میتوانید چیزی شبیه به این انجام دهید:

به عنوان مثال

class UsersApi {

    public ajax;

    constructor(ajax) {
      this.ajax = ajax;
    }

    getAllUsers() {
      return this.ajax.get(....)
    }

}

ویرایش: عبور دادن وابستگیها (که به آن تزریق وابستگی هم گفته میشود) یکی از مواردی است که باعث میشود ماژولهایی مثل این به طور قابل توجهی راحتتر تست شوند - آن را در نظر بگیرید!

سپس میتوانستید به راحتی تستهای خود را به این صورت شبیهسازی کنید:

  const someSuccessfulResponse = ...
  const someFailedResponse = ...

  const ajaxWithSuccess = {
     get:jest.fn(() => of(someSuccessfulResponse))
  }

  const ajaxWithFailed = {
     get:jest.fn(() => throwError(someFailedResponse))
  }

  describe('my test suite',() => {

    it("should test a successful response", (done) => {

        const user = new UsersApi(ajaxWithSuccess);
        user.getAllUsers().subscribe(d => {
         expect(d).toBe(someSuccessfulResponse);
         done();
       });

    });

    it("should test a failed response", (done) => {

        const user = new UsersApi(ajaxWithFailed);
        user.getAllUsers().subscribe(null,error => {
         expect(d).toBe(someFailedResponse);
         done();
       });

    });

  });

توجه: شما نباید بخواهید درخواست واقعی API را آزمایش کنید. شما میخواهید بررسی کنید که کد شما به درستی با هر پاسخ API که فکر میکنید ممکن است دریافت کند، برخورد میکند. به این فکر کنید، چگونه میخواهید بررسی کنید که پاسخ ناموفق API توسط کد شما به درستی مدیریت میشود اگر API شما همیشه کد 200 برگرداند؟

ویرایش #۲۷: کد بالا برای من وقتی که jest را اجرا میکنم به خوبی کار میکند، کاملاً مشخص نیست که چرا jasmine (jest روی jasmine اجرا نمیشود؟) میگوید نمیتواند async را در it انجام دهد. به هر حال، میتوانید فقط کد بالا را تغییر دهید تا همه چیز را در beforeEach تنظیم کنید و فقط expect خود را در it انجام دهید.