ค้นหาเว็บไซต์

javascript - Jest toHaveBeenNthCalledWith ส่งค่าตัวแปรตามค่าที่ถูกตั้งไว้ในปัจจุบัน แทนที่จะเป็นค่าตอนที่ถูกเรียก


ฉันกำลังพัฒนาแอปพลิเคชัน nestjs โดยใช้ชุดปลั๊กอิน nestjs มาตรฐานทั่วไป (เช่น @nestjs/axios และ @nestjs/testing)

ฉันมีฟังก์ชันที่ค่อนข้างเรียบง่ายซึ่งควรจะแบ่งหน้า (paginate) ผ่านคำขอ API และเพิ่มค่าที่ได้ลงในอาร์เรย์

async syncRecordList(limit: number = 1000) {
        const url = 'https://my.test.url/records';
        const headers = {
            Authorization: 'Bearer token-value',
        };
        const params = {
            limit, // the per page limit
            offset: 0, // the offset for pagination
        };
        const itemIds: number[] = [];
        let more: boolean = true;
        while (more) {
            const response = await this.httpService.axiosRef.get(url, {
                headers,
                params,
            });
            response.data.items.forEach((item) => {
                itemIds.push(item.id);
            });
            params.offset += limit;
            more = response.data.hasMore;
        }
        return itemIds;
    }

เพื่อทดสอบฟังก์ชันง่าย ๆ นี้ ฉันมีการทดสอบดังต่อไปนี้:

describe('syncRecordList', () => {
        it('should call multiple times if there is pagination', async () => {
            const response0 = {
                data: {
                    items: [{ id: 1 }, { id: 2 }],
                    hasMore: true,
                },
            };

            const response1 = {
                data: {
                    items: [{ id: 3 }, { id: 4 }],
                    hasMore: false,
                },
            };

            const callExpectWith0 = [
                'https://my.test.url/records',
                {
                    headers: {
                        Authorization: 'Bearer token-value',
                    },
                    params: {
                        limit: 2,
                        offset: 0
                    },
                },
            ]

            const callExpectWith1 = [
                'https://my.test.url/records',
                {
                    headers: {
                        Authorization: 'Bearer token-value',
                    },
                    params: {
                        limit: 2,
                        offset: 2
                    },
                },
            ]

            const httpSpy = jest.spyOn(service.httpService.axiosRef, 'get')
                .mockResolvedValueOnce(response0)
                .mockResolvedValueOnce(response1);

            await service.syncRecordList(2);

            expect(httpSpy).toHaveBeenCalledTimes(2);
            expect(httpSpy).toHaveBeenNthCalledWith(1, ...callExpectWith0);
            expect(httpSpy).toHaveBeenNthCalledWith(2, ...callExpectWith1);
        });
    });

อย่างที่คุณเห็นจากโค้ด ฉันคาดว่าการเรียกครั้งแรกจะใช้พารามิเตอร์ limit: 2, offset: 0 และการเรียกครั้งที่สองจะใช้พารามิเตอร์ limit: 2, offset: 2 โค้ดถูกออกแบบให้ค่าพารามิเตอร์เปลี่ยนไปในแต่ละครั้งที่มีการเรียกใช้ลูป เพื่อปรับค่า offset ซึ่งสิ่งนี้เกิดขึ้นก่อนที่ลูปจะสิ้นสุด นั่นหมายความว่าครั้งที่สอง ก่อนที่ค่าของ more จะถูกตั้งค่าเป็น false เราจะเพิ่มค่าพารามิเตอร์ offset เพื่อคาดการณ์การเรียกลูปครั้งต่อไป

ความคาดหวังของฉันคือสิ่งนี้จะผ่าน ในประวัติการโทรของม็อกนั้น ฉันคาดหวังว่าพารามิเตอร์จะแสดงสองค่าที่แตกต่างกัน อย่างไรก็ตาม สิ่งที่ฉันเห็นจริงกลับเป็นความล้มเหลว

expect(jest.fn()).toHaveBeenNthCalledWith(n, ...expected)

    n: 1
    Expected: "https://my.test.url/records", {"headers": {"Authorization": "Bearer token-value"}, "params": {"limit": 2, "offset": 0}}
    Received
    ->     1
              "https://my.test.url/records",
            @@ -2,8 +2,8 @@
                "headers": Object {
                  "Authorization": "Bearer token-value",
                },
                "params": Object {
                  "limit": 2,
            -     "offset": 0,
            +     "offset": 4,
                },
              },
           2: "https://my.test.url/records", {"headers": {"Authorization": "Bearer token-value"}, "params": {"limit": 2, "offset": 4}}

ผลลัพธ์ของ console.log(JSON.stringify(httpSpy.mock.calls)); คือ [["https://123456789c.suitetalk.api.netsuite.com/services/rest/record/v1/assemblyItem",{"headers":{"Authorization":"Bearer token-value"},"params":{"limit":2,"offset":4}}],["https://123456789c.suitetalk.api.netsuite.com/services/rest/record/v1/assemblyItem",{"headers":{"Authorization":"Bearer token-value"},"params":{"limit":2,"offset":4}}]] แสดงให้เห็นว่ามันถูกเรียกสองครั้งโดยมี offset เท่ากับสี่

แต่ไม่ได้ถูกเรียกด้วยการเลื่อนสี่ ค่าของ params ในตอนท้ายของการทดสอบมีการเลื่อนสี่

ถ้าฉันเปลี่ยนโค้ดเล็กน้อย เพื่อใช้สำเนาของ params แทน params มันจะทำงาน:

    async syncRecordList(type: NetSuiteRecordTypes, integrationId: string, limit: number = 1000) {
        // ... Snipped 
        const params = {
            limit, // the per page limit
            offset: 0, // the offset for pagination
        };
        const itemIds: number[] = [];
        let more: boolean = true;
        while (more) {
            // This was done so that testing would pass. The params object was being modified and
            // causing the tests to fail as it seems to always refer to the variable used, not what what
            // was actually called with.
            const paramsToUse = { ...params };
            const response = await this.httpService.axiosRef.get(url, {
                headers,
                params: paramsToUse,
            });
            response.data.items.forEach((item) => {
                itemIds.push(item.id);
            });
            params.offset += limit;
            more = response.data.hasMore;
        }
        return itemIds;
    }

ในกรณีนี้ การทดสอบของฉันผ่านเพราะฟังก์ชันถูกเรียกด้วยสิ่งที่ฉันคาดไว้

ท้ายที่สุดคำถามของฉันคือ มีวิธีที่ Jest เรียกใช้กับ can ที่สามารถใช้สิ่งที่ถูกส่งเข้ามาในขณะแห่งการเรียกจริงๆ หรือไม่ ไม่ใช่ค่าของตัวแปรหลังจากที่การทดสอบเสร็จสิ้น?

คำตอบ #1

นี่เป็นข้อจำกัดของ Jest จริง ๆ ความคิดเห็นในปัญหา Jest GitHub หมายเลข 434 และ 4715 ระบุว่าการคัดลอกค่ามาแทนการใช้การอ้างอิงจะมีค่าใช้จ่ายสูงเกินไปและส่งผลกระทบต่อประสิทธิภาพของ Jest อย่างรุนแรง

จากความคิดเห็นต่อคำถามเดิมของฉัน ดูเหมือนว่าฉันมีตัวเลือกไม่กี่อย่าง

  1. แก้ไขโค้ดของฉันให้ตัวแปรในลูปมีค่าที่แตกต่างกันเสมอ แทนที่จะเป็นการอ้างอิง
  2. แก้ไขการทดสอบของฉันเพื่อใช้ assert ที่แตกต่าง
  3. ใช้ไลบรารีที่แตกต่างกันเพื่อเปลี่ยนการทดสอบนั้น

ในคำถามต้นฉันกำลังทำตัวเลือกที่ 1 อยู่แล้ว แต่ฉันได้ปรับปรุงสิ่งนั้นตามความคิดเห็นต่อคำถาม สุดท้ายฉันก็ได้สิ่งที่ประมาณนี้ในวิธีของฉัน:

async syncRecordList(limit: number = 1000) {
        const url = 'https://my.test.url/records';
        const headers = { Authorization: 'Bearer token-value' };
        const itemIds: number[] = [];
        let offset: number = 0;
        let more: boolean = true;
        while (more) {
            const response = await axios.get(url, {
                headers,
                params: { limit, offset },
            });
            response.data.items.forEach((item) => {
                itemIds.push(item.id);
            });
            offset += limit;
            more = response.data.hasMore;
        }
        return itemIds;
    }