赛普拉斯xhr请求重试ontimeout [英] cypress xhr request retry ontimeout

查看:93
本文介绍了赛普拉斯xhr请求重试ontimeout的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试使以下 XHR 请求重试(如果超时).如果删除 cy.wait('@ formRequest'),似乎测试不会停止以等待正确的响应.但是,如果我将 cy.wait 留在其中,则如果请求超时,测试将失败.因此,我尝试向 cy.request 调用中添加一些回调(ontimeout)(如第二个代码块所示),但这似乎也不起作用.知道如果响应不是200还是根本没有收到响应,如何使cypress命令重试?

I am trying to make the following XHR request retry if it times out. If I remove the cy.wait('@formRequest'), it seems like the test doesn't stop to wait for the response properly. But if I leave the cy.wait in, the test will fail if the request times out. So I tried to add some callbacks (ontimeout) to the cy.request call (shown in the second code block), but that doesn't seem to work either. Any idea how I can make this cypress command retry if the response is not 200 OR if a response is not received at all??

我对此还很陌生,因此请您多加解释.

I'm pretty new to this, so some explanation would be greatly appreciated.

提前谢谢!

// this works, but doesn't handle timeout
Cypress.Commands.add('form_request', (url, formData) => {
  function recursive_request (url, formData, attempt) {
    let xhr
    const maxRetry = 3

    if (attempt > maxRetry) {
      throw new Error(`form_request() max retry reached, ${maxRetry}`)
    }

    if (attempt !== 0) {
      cy.log(`Backing off retry. Attempt: ${attempt}/${maxRetry}.`)
      cy.wait(30000)
    }

    return cy
      .server()
      .route('POST', url)
      .as('formRequest')
      .window()
      .then((win) => {
        xhr = new win.XMLHttpRequest()
        xhr.open('POST', url)
        xhr.send(formData)
      })
      .wait('@formRequest')
      .then((resp) => {
        if (xhr.status !== 200) {
          return recursive_request(url, formData, attempt + 1)
        }

        return resp
      })
  }

  return recursive_request(url, formData, 0)
})

Cypress.Commands.add('form_request', (url, formData) => {
  function recursive_request (url, formData, attempt) {
    let xhr
    const maxRetry = 3

    if (attempt > maxRetry) {
      throw new Error(`form_request() max retry reached, ${maxRetry}`)
    }

    if (attempt !== 0) {
      cy.log(`Backing off retry. Attempt: ${attempt}/${maxRetry}.`)
      cy.wait(30000)
    }

    return cy
      .server()
      // .route('POST', url)
      .route({
        'method': 'POST',
        'url': url
      }, {
        ontimeout: (xhr) => {
            return new Cypress.Promise  ((resolve) => {
              resolve(recursive_request(url, formData, attempt + 1))
            })
            
        },
        onreadystatechange: (xhr) => {
          if (xhr.status !== 200 && xhr.readyState === 4) {
            return new Cypress.Promise  ((resolve) => {
              resolve(recursive_request(url, formData, attempt + 1))
            })
          }
        }
      })
      .as('formRequest')
      .window()
      .then((win) => {
        xhr = new win.XMLHttpRequest()
        xhr.open('POST', url)
        xhr.send(formData)
      })
      .wait('@formRequest')
      .then((resp) => {
        if (xhr.status !== 200) {
          return recursive_request(url, formData, attempt + 1)
        }

        return resp
      })
  }

  return recursive_request(url, formData, 0)
})

我试图制作以下自定义cypress命令.但是,我收到了非法调用错误.浏览jQuery.ajax()上的文档,对我来说尚不清楚什么是非法的.

edit: I tried to make the following custom cypress command. However, I am getting an illegal invocation error. Looking through the documentation on jQuery.ajax(), it's not clear to me what is illegal about it.

我还有几个问题...#1.传入{timeout:30000}对象如何工作?这个叫什么?我对诺言链接的基本理解是,您传入一些处理程序函数以使用先前调用产生的任何内容.我以前没有看过 .then(arg,function(){...})模式,这叫什么?

I also have a couple questions... #1. how does passing in the {timeout: 30000} object work? What is this called? My basic understanding of promise chaining is that you pass in some handler function to use whatever the previous call yielded. I have not seen the pattern .then(arg, function () {...}) before, what is this called?

#2.cy.log(...)在这里可以工作吗?我怀疑不会,因为那样会混合同步/异步代码,对吗?(对不起,我现在就自己玩,但是我无法让它克服非法调用错误). const log = Cypress.log(...)在这里可以同步工作吗?

#2. will cy.log(...) work here? I suspect that it won't because that would be mixing sync/async code, correct? (sorry, I would play with it myself right now but I can't get it to run past the illegal invocation error). Would const log = Cypress.log(...) work instead synchronously here?

  527 |       }
  528 | 
> 529 |       Cypress.$.ajax({
      |                 ^
  530 |         url,
  531 |         method: 'POST',
  532 |         data: formData,

Cypress.Commands.add('form_request', (url, formData) => {
  const waits = { request: 2000, retry: 10000 }
  const maxRetry = 3

  function recursive_request(url, formData, attempt = 0) {

    return new Cypress.Promise((resolve) => {
      if (attempt > maxRetry) {
        throw new Error(`form_request() max retry reached, ${maxRetry}`)
      }

      const retry = (reason) => {
        console.log(`${reason} - retrying in ${waits.retry}`) // #2
        return setTimeout(() => {
          recursive_request(url, formData, attempt + 1)
            .then(data => resolve(data)); // resolve from recursive result
        }, waits.retry)
      }

      Cypress.$.ajax({
        url,
        method: 'POST',
        data: formData,
        timeout: waits.request,
      })
        .done(function (data, textStatus, jqXHR) {
          if (textStatus !== 'success') {
            retry('Status not success')
          }
          resolve(data)
        })
        .catch(function (jqXHR, textStatus, errorThrown) {
          retry(errorThrown)
        })
    })
  }

  cy.wrap(null)
    .then({ timeout: 30000 }, // #1
      () => {
        return recursive_request(url, formData)
      })
    .then((result) => {
      return result
    })
})

推荐答案

测试级重试

我会看一下测试重试(Cypress v 5.0+)处理超时问题,

I would take a look at test-retries (Cypress v 5.0+) to handle the timeout issue,

const waitTime = 1000; // shortened for demo 

Cypress.Commands.add('form_request', (url, formData) => {

  return cy
    .request({
      method: 'POST', 
      url,
      body: formData,
      timeout: waitTime
    })
    .then((resp) => {
      if (resp.status !== 200) {
        throw new Error(`Status not 200`)
      }
      return resp
    })
})

let attempts = 0;

it('repeats', { retries: 3 }, () => {

  const wait = attempts ? waitTime : 0;  // initial wait = 0
  const url = attempts < 2
    ? 'http://nothing'                   // initially use a non-reachable url
    : 'http://example.com';              // 3rd attempt succeeds
  attempts++;
  cy.wait(wait).form_request(url, { name: 'Fred' })

})


请求级重试

如果要在请求级别(而不是测试级别)重试,则@Ackroydd是正确的-您应该放弃 cy.route(),因为总之,赛普拉斯命令不是旨在捕捉失败.

If you want to retry at the request level (not the test level), then @Ackroydd is correct - you should ditch the cy.route() because, in short, Cypress commands are not built to catch fails.

这是使用 jquery.ajax 的基本示例.您可能会使用 axios.retry 之类的东西来获得更简洁的功能.

Here is a basic example using jquery.ajax. You may get a more succinct function using something like axios.retry.

请参阅等待承诺,使用了柏树图案.

Refer to Waiting for promises for the Cypress pattern used.

递归请求

const waits = { request: 2000, retry: 10000 } 
const maxRetry = 3

function recursive_request(url, formData, attempt = 0) {

  return new Cypress.Promise ((resolve) => {
    if (attempt > maxRetry) {
      throw new Error(`form_request() max retry reached, ${maxRetry}`)
    }
  
    const retry = (reason) => {
      console.log(`${reason} - retrying in ${waits.retry}`);
      return setTimeout(() => {
        recursive_request(url, formData, attempt + 1)
          .then(data => resolve(data)); // resolve from recursive result
      }, waits.retry)
    }
    
    Cypress.$.ajax({
      url,
      method: 'POST',
      data: formData,
      timeout: waits.request,
    })
    .done(function(data, textStatus, jqXHR) {
      if (textStatus !== 'success') {
        retry('Status not success')
      }
      resolve(data)
    })
    .catch(function(jqXHR, textStatus, errorThrown) {
      retry(errorThrown)
    })
  })
}

测试

it('retries form post', () => {

  cy.wrap(null)
    .then(
      { timeout: 30000 },   // timeout must be above maximum expected, 
                            // i.e maxRetry * waits.retry
      () => recursive_request('http://localhost:3000', { name: 'Fred' })
    )
    .then(result => console.log(result))

})

用于测试的脆弱服务器

const express = require("express");
const app = express();
const port = 3000;

const obj = {
  name: 'Fred'
};

let attempt = 0;

app.post("*", (req, res) => {
  if (attempt >= 3) {
    attempt = 0;   // reset to allow multiple test runs
    res.status(201).json(obj)
  }
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

这篇关于赛普拉斯xhr请求重试ontimeout的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆