简体   繁体   中英

azure function unit test cases for retry scenario - nodejs

I have built an azure function using NodeJs. And I am Using Jest for testing.

Scenario: When making API calls from function, if the 3rd party returns Timeout, I need to retry 1 more time and then quit. This is working fine when live and server is retrying automatically as expected.

I am failing to write test cases as getting an Jest Timeout error.

Logic:

function.json

"retry": {
    "strategy": "exponentialBackoff",
    "maxRetryCount": 1,
    "minimumInterval": "00:00:10",
    "maximumInterval": "00:00:40"
}

index.js - under try/catch

catch (err) {
                let errorCode = err.errorCode || err.code || 500;                   
                let errorMessage = err.message|| err.errorMessage;
                if (errorMessage && errorMessage.indexOf("ETIMEDOUT") >= 0 ) {
                    errorCode = 429; //setting errorCode as 429 to retry automatically from azure function
                    let retryError = new Error("retrying requests");
                    retryError.code = 429;
                    throw retryError;
                }else{
                   context.done();
                }
           };

index.test.js

Test suite failed to run retrying requests

The test class breaks at this line " let retryError = new Error("retrying requests"); " once the error is thrown from main class.

So, if the server retry exhausted and still the response is 429, how to write test cases?

I am following this Doc to implement automated test in Nodejs Azure function. Steps follow:

To test the Azure function in VS code extension, use Jest.

Install jest using npm i jest

Update the package.json to replace the existing test command with the below command

"scripts": {
    "test": "jest"
}

For automating tests you can create a module to run tests. Create a new folder which you prefer ( testing ) Add a new file ( defaultContext.js ) in a created folder and add the below command to log the function default execution context.

module.exports = {
    log: jest.fn()
};

在此处输入图像描述

After that add a new file index.test.js in the HTTP function trigger folder and add the below code

const httpFunction = require('./index');
const context = require('../testing/defaultContext')
test('Http trigger should return known text', async () => {
    const request = {
    query: { name: 'Bill' }
};
await httpFunction(context, request);
expect(context.log.mock.calls.length).toBe(1);
expect(context.res.body).toEqual('Hello Bill');
});

Use the npm test command to run the unit test.

Try adding retry policy in your azure function

Add retry policy in function.json in your trigger use fixedDelay or exponentialBackoff

{
    "disabled": false,
    "bindings": [
    {
        ....
    }
    ],
    "retry": {
        "strategy": "exponentialBackoff",
        "maxRetryCount": 5,
        "minimumInterval": "00:00:10",
        "maximumInterval": "00:15:00"
    }
}

在此处输入图像描述

After adding all run the test using npm test I could see the results:

在此处输入图像描述

Retry limitations:

In the consumption plan,

the app may be scaled down to zero while retrying the final messages in a queue. &

the app may be scaled down while performing retries. Choose a retry interval <= 00:01:00 and <= 5 retries for better result.

Warning It is not recommended to set the delivery count for a trigger like Service Bus Queues to 1, meaning the message would be dead-lettered immediately after a single function retry cycle. This is because triggers provide resiliency with retries, while the function retry policy is best effort and may result in less than the desired total number of retries.

Refer here

Just modified the structure to catch every error as below:

try {
                    await retry(
                        async(bail) => {
                            api_attempt++;
                            console.log('correlationId:' + correlationId + ' api_attempt:' + api_attempt);
                            let api_Resp = await myAPI(correlationId, payload)
                                .catch((error) => {
                                    console.error("correlationId:" + correlationId + " myAPI caught error::", JSON.stringify(error));
                                    let errorCode = error.statusCode;
                                    let errorMessage = error.errorMessage;
                                    let errorDescription = error.errorDescription;
                                    console.log("correlationId:" + correlationId + " myAPI errorCode::" + errorCode);
                                    if (errorCode === 429 ||
                                        (errorMessage && (errorMessage.indexOf("ETIMEDOUT") >= 0 || errorMessage.indexOf("ECONNREFUSED") >= 0 || errorMessage.indexOf("Timeout") >= 0)) ||
                                        (errorDescription && (errorDescription.indexOf("ETIMEDOUT") >= 0 || errorDescription.indexOf("ECONNREFUSED") >= 0 || errorDescription.indexOf("Timeout") >= 0 || errorDescription.indexOf("retries exhausted") >= 0))
                                    ) {
                                        console.log("correlationId:" + correlationId + " retrying myAPI");
                                        throw error; //to retry
                                    } else if (errorCode === 500) {
                                        console.log("correlationId:" + correlationId + " exiting myAPI and not writing to db");
                                        return; //don't bail and don't write to DB (as 3Party writes to DB for 500 status)
                                    } else {
                                        // don't retry by bailing error 
                                        console.log("correlationId:" + correlationId + " not attempting myAPI retry..");
                                        bail(error);
                                        return;
                                    }
                                })
                        }, retryOptions);
                } catch (err) {
                    console.error("correlationId:" + correlationId + " myAPI final error::api_attempt:" + api_attempt + " - retries exhausted while calling API::", JSON.stringify(err));
                    let errorCode = err.statusCode || 500;
                    let errorMessage = err.code || err.errorMessage || err.message || err;
                    let errorDescription = err.statusMessage || err.errorDescription || errorMessage || "Unknown Error at 3Party Single API";
                    //writing errors to db
                    callDB(false, correlationId, payload, 'AzureFunction', 'Request failed at 3Party', errorCode, errorMessage, errorDescription);
                }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM