偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

開(kāi)源 | Canyon:提升JavaScript代碼質(zhì)量的全面覆蓋率分析工具

人工智能 開(kāi)源
從這篇文章發(fā)表時(shí)起,我們將正式開(kāi)源Canyon。JavaScript是時(shí)下最流行的編程語(yǔ)言,但是端到端測(cè)試覆蓋率收集領(lǐng)域一直空白,我們的代碼開(kāi)發(fā)基于了istanbuljs,monaco editor等優(yōu)秀開(kāi)源項(xiàng)目。

作者簡(jiǎn)介

wr_zhang25,攜程資深前端開(kāi)發(fā)工程師,關(guān)注前端代碼覆蓋率、JavaScript開(kāi)源方向。

Liang, 攜程資深研發(fā)經(jīng)理,質(zhì)量專家,專注質(zhì)量工程領(lǐng)域。

一、背景

istanbuljs 是一款優(yōu)秀的JavaScript代碼覆蓋率工具,主要用于單元測(cè)試的代碼覆蓋率檢測(cè)和生成本地覆蓋率報(bào)告。然而,隨著現(xiàn)代前端技術(shù)和UI自動(dòng)化測(cè)試的發(fā)展,對(duì)端到端測(cè)試的代碼覆蓋率檢測(cè)需求逐漸增加,istanbuljs提供的功能顯得捉襟見(jiàn)肘。

在攜程內(nèi)部JavaScript代碼覆蓋率使用的是gitlab內(nèi)置的coverage上報(bào),也是只支持單元測(cè)試的覆蓋率收集和概覽數(shù)據(jù)展示。隨著攜程的前端技術(shù)日益精進(jìn),我們有了自己的前端流量錄制平臺(tái),并且部署了相當(dāng)大規(guī)模的模擬器集群進(jìn)行UI自動(dòng)化(flybirds)回放。這種場(chǎng)景下,需要對(duì)端到端測(cè)試的代碼覆蓋率進(jìn)行收集和展示,以便開(kāi)發(fā)同學(xué)更好的了解到自己的代碼質(zhì)量。

傳統(tǒng)的istanbuljs提供的功能已經(jīng)無(wú)法滿足我們的需求。我們需要處理UI自動(dòng)化過(guò)程中來(lái)自前端高并發(fā)的覆蓋率上報(bào),實(shí)時(shí)的覆蓋率聚合,以及覆蓋率數(shù)據(jù)的聚合展示。因此,我們?cè)贗stanbuljs的基礎(chǔ)上開(kāi)發(fā)了Canyon,解決端到端測(cè)試覆蓋率難收集的問(wèn)題。

目前,攜程的多個(gè)部門(mén)已經(jīng)開(kāi)始使用Canyon,并在持續(xù)集成流水線構(gòu)建階段插入探針代碼,在UI自動(dòng)化測(cè)試階段收集和上報(bào)覆蓋率數(shù)據(jù)。服務(wù)端實(shí)時(shí)生成詳盡的覆蓋率報(bào)告,為UI自動(dòng)化測(cè)試用例提供全面的覆蓋率數(shù)據(jù)指標(biāo)。

二、介紹

Canyon 通過(guò)簡(jiǎn)單的 Babel 插件配置即可實(shí)現(xiàn)代碼插裝、覆蓋率上報(bào)和實(shí)時(shí)報(bào)告生成。其技術(shù)棧完全基于 JavaScript,只需 Node.js 環(huán)境即可運(yùn)行,部署方便,適用于云原生環(huán)境的部署(如 Docker、Kubernetes)。

應(yīng)用的架構(gòu)設(shè)計(jì)適用于處理高頻、大規(guī)模的覆蓋率數(shù)據(jù)上報(bào),能夠應(yīng)對(duì) UI 自動(dòng)化測(cè)試中的各種場(chǎng)景。同時(shí),Canyon 與現(xiàn)有的 CI/CD 工具(如 GitLab CI、Jenkins)無(wú)縫集成,使用戶能夠輕松地在持續(xù)集成流水線中使用。

架構(gòu)圖如下:

圖片

下面會(huì)根據(jù)以下幾個(gè)部分來(lái)介紹 Canyon 的主要功能:

  • 代碼覆蓋率
  • 代碼插樁
  • 測(cè)試與上報(bào)
  • 覆蓋率聚合
  • 覆蓋率報(bào)告
  • 變更代碼覆蓋率
  • react native 覆蓋率收集方案
  • 覆蓋率提升優(yōu)先級(jí)列表

三、代碼覆蓋率

隨著編寫(xiě)更多的end-to-end測(cè)試case,你會(huì)發(fā)現(xiàn)有一些疑問(wèn),我需要寫(xiě)更多的測(cè)試用例嗎?究竟還有哪些代碼沒(méi)測(cè)到?用例會(huì)不會(huì)重復(fù)了?這個(gè)時(shí)候代碼覆蓋率就派上用場(chǎng)了,它的原理是在代碼執(zhí)行前將代碼探針插入到源代碼中(其實(shí)就是上下文加計(jì)數(shù)器),這樣每當(dāng)case執(zhí)行的時(shí)候就可以觸發(fā)其中的計(jì)數(shù)器。

在代碼中插入代碼探針的步驟稱為代碼插樁(instrument)。插樁前的代碼:

// add.js
function add(a, b) {
  return a + b
}
module.exports = { add }

插樁過(guò)程是對(duì)代碼解析以查找所有函數(shù)、語(yǔ)句和分支,然后將計(jì)數(shù)器插入代碼中。對(duì)于上面的代碼,插樁完成后:

// 這個(gè)對(duì)象用于計(jì)算每個(gè)函數(shù)和每個(gè)語(yǔ)句被執(zhí)行的次數(shù)
const c = (window.__coverage__ = {
  // "f" 記錄每個(gè)函數(shù)被調(diào)用的次數(shù)
  f: [0],
  // "s" 記錄每個(gè)語(yǔ)句被調(diào)用的次數(shù)
  // 我們有3個(gè)語(yǔ)句,它們都從0開(kāi)始
  s: [0, 0, 0],
})


// 第一個(gè)語(yǔ)句定義了函數(shù)
c.s[0]++
function add(a, b) {
  // 函數(shù)被調(diào)用后是第二個(gè)語(yǔ)句
  c.f[0]++
  c.s[1]++


  return a + b
}
// 第三個(gè)語(yǔ)句即將被調(diào)用
c.s[2]++
module.exports = { add }

我們希望確保文件中的每個(gè)語(yǔ)句和函數(shù)add.js都已被我們的測(cè)試至少執(zhí)行一次。因此我們編寫(xiě)一個(gè)測(cè)試:

// add.cy.js
const { add } = require('./add')


it('adds numbers', () => {
  expect(add(2, 3)).to.equal(5)
})

當(dāng)測(cè)試調(diào)用時(shí)add(2, 3),執(zhí)行“add”函數(shù)內(nèi)的計(jì)數(shù)器遞增,覆蓋范圍對(duì)象變?yōu)椋?/span>

{
  f: [1],
  s: [1, 1, 1]
}

這個(gè)測(cè)試用例覆蓋率達(dá)到了100%,每個(gè)函數(shù)和每個(gè)語(yǔ)句都至少執(zhí)行了一次。但是在實(shí)際應(yīng)用中,要達(dá)到100%的代碼覆蓋率需要多次測(cè)試。

這是覆蓋率的基本介紹,有了這個(gè)前置知識(shí),方便大家理解下面的內(nèi)容。

四、代碼插樁(instrumenting-code)

代碼覆蓋率最重要的一環(huán)就是代碼插樁

istanbuljs 是久經(jīng)沙場(chǎng)的js代碼插樁黃金標(biāo)準(zhǔn)。Canyon主要為端到端測(cè)試提供解決方案,經(jīng)過(guò)大量的實(shí)驗(yàn)驗(yàn)證,現(xiàn)代化前端工程的覆蓋率插樁必須要編譯時(shí)插樁。具體原因是istanbuljs提供的nyc插樁工具只能對(duì)原生js進(jìn)行插樁,然而前端模版語(yǔ)法層出不窮,例如ts、tsx、vue,雖然nyc也可以插樁,但是結(jié)構(gòu)實(shí)踐證明直接插樁的覆蓋率效果不盡人意,無(wú)法精確到該插樁到的函數(shù)、語(yǔ)句、分支。

幸運(yùn)的是經(jīng)過(guò)調(diào)研,我們發(fā)現(xiàn)了babel-plugin-istanbul、vite-plugin-istanbul(experimental)、swc-plugin-coverage-instrument(experimental)。等類型工程的插樁解決方案。這些方案無(wú)一例外都是在前端工程編譯階段在將代碼分析成ast抽象語(yǔ)法樹(shù)的時(shí)候在適當(dāng)時(shí)機(jī)進(jìn)行插樁方法調(diào)用,更精確的插樁到的函數(shù)、語(yǔ)句、分支。

適用的工程類型:

工程類型

方案

vanilla javascript

nyc

babel

babel-plugin-istanbul

vite

vite-plugin-istanbul (experimental)

swc

swc-plugin-coverage-instrument (experimental)

用戶可以根據(jù)自己的工程類型選擇合適的插樁方案,只需要在工程中安裝對(duì)應(yīng)的插件,然后就會(huì)在編譯時(shí)自動(dòng)插樁。

以babel.config.js為例:

module.exports = {
  plugins: [
    [
      'babel-plugin-istanbul',
      {
        exclude: ['**/*.spec.js', '**/*.spec.ts', '**/*.spec.tsx', '**/*.spec.jsx'],
      },
    ],
  ],
};

插樁完成后,代碼中會(huì)插入一些代碼探針,這些代碼探針會(huì)在運(yùn)行時(shí)收集覆蓋率數(shù)據(jù),然后上報(bào)到Canyon服務(wù)端。

檢查是否插樁成功,可以在編譯后的產(chǎn)物中搜索__coverage__,如果有則說(shuō)明插樁成功。

圖片

為了緊密關(guān)聯(lián)插樁代碼的源代碼,我們適配了各種provider,將環(huán)境變量發(fā)送到Canyon服務(wù)端,兌換到reportID,方便覆蓋率數(shù)據(jù)聚合計(jì)算完成后的覆蓋率源文件的關(guān)聯(lián)展示。

我們還提供了babel-plugin-canyon的babel插件,可以在各種流水線內(nèi)(aws,gitlab ci)讀取環(huán)境變量(branch、sha),以供后續(xù)覆蓋率數(shù)據(jù)與對(duì)應(yīng)的gitlab源代碼關(guān)聯(lián)。

babel.config.js

module.exports = {
  plugins: [
    [
        'babel-plugin-canyon',
        {
          provider: 'gitlab',
          branch: process.env.CI_COMMIT_REF_NAME,
          sha: process.env.CI_COMMIT_SHA,
        },
    ],
  ],
};

支持的提供商:

  • Azure Pipelines
  • CircleCI
  • Drone
  • Github Actions
  • GitLab CI
  • Jenkins
  • Travis CI

需要特別注意的是,代碼探針的插樁會(huì)在構(gòu)建產(chǎn)物上下文加上代碼探針,會(huì)是代碼整體產(chǎn)物增大30%,建議不要上生產(chǎn)環(huán)境。

五、測(cè)試與上報(bào)

當(dāng)插樁完成發(fā)布到測(cè)試環(huán)境后,我們就可以進(jìn)行測(cè)試了。拿playwright舉例,對(duì)于插樁成功的前端應(yīng)用站點(diǎn),window對(duì)象上面都會(huì)掛載__coverage__和__canyon__對(duì)象,我們需要在playwright測(cè)試過(guò)程中收集并上報(bào)這些數(shù)據(jù)到canyon的服務(wù)端。

playwright示例:

const {chromium} = require('playwright');
const main = async () => {
  const browser = await chromium.launch()
  const page = await browser.newPage();
  // 進(jìn)入被測(cè)頁(yè)面
  await page.goto('http://test.com')
  // 執(zhí)行測(cè)試用例
  // 用例1
  await page.click('button')
  // 用例2
  await page.fill('input', 'test')
  // 用例3
  await page.click('text=submit')
  const coverage = await page.evaluate(`window.__coverage__`)
  // 收集上報(bào)覆蓋率
  upload(coverage)
  browser.close()
}


main()

攜程內(nèi)部有自己的UI自動(dòng)化平臺(tái) flybirds,我們?cè)趂lybirds內(nèi)部集成了Canyon覆蓋率數(shù)據(jù)的收集和上報(bào)。真實(shí)的瀏覽器UI自動(dòng)化測(cè)試的覆蓋率收集場(chǎng)景較為復(fù)雜,主要體現(xiàn)在多頁(yè)面(MPA)的覆蓋率收集時(shí)機(jī)不確定性。

單頁(yè)面(SPA)與多頁(yè)面(MPA)

當(dāng)測(cè)試用例執(zhí)行完成后,對(duì)于單頁(yè)面應(yīng)用(SPA)或者多頁(yè)面應(yīng)用而言,上報(bào)步驟是將頁(yè)面window對(duì)象上的__coverage__對(duì)象上報(bào)到Canyon服務(wù)端,對(duì)于單頁(yè)面應(yīng)用來(lái)說(shuō),相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,在所有測(cè)試內(nèi)容都在單頁(yè)面應(yīng)用內(nèi),覆蓋率數(shù)據(jù)會(huì)常駐在window對(duì)象中,對(duì)于多頁(yè)面應(yīng)用而言,路由的跳轉(zhuǎn)會(huì)導(dǎo)致window對(duì)象的重制,丟失coverage對(duì)象。所以這個(gè)時(shí)機(jī)是至關(guān)重要的,經(jīng)過(guò)大量實(shí)踐驗(yàn)證,我們找到了瀏覽器的onvisiblechange方法。

  • visibilitychange

在瀏覽器可見(jiàn)性改變的時(shí)候上報(bào)覆蓋率數(shù)據(jù),值得一提的是,對(duì)于visibilitychange這種可能會(huì)導(dǎo)致重復(fù)數(shù)據(jù)上報(bào),但是對(duì)于覆蓋率統(tǒng)計(jì)來(lái)說(shuō),未執(zhí)行到的代碼多次合并來(lái)說(shuō)不會(huì)影響覆蓋率的具體指標(biāo)數(shù)據(jù)統(tǒng)計(jì)。

  • fetchLater

Chrome 瀏覽器正在積極引入一個(gè)革命性的 JavaScript API——fetchLater()。這個(gè)全新的 API 旨在徹底簡(jiǎn)化關(guān)閉頁(yè)面時(shí)的數(shù)據(jù)發(fā)送過(guò)程,確保即使在頁(yè)面關(guān)閉后或用戶離開(kāi)的情況下,請(qǐng)求也能在未來(lái)某個(gè)時(shí)刻被安全、可靠地發(fā)出。

這個(gè)API的推出時(shí)令人振奮的,可以很好的解決多頁(yè)面(MPA)收集難的問(wèn)題,只需要在瀏覽器關(guān)閉時(shí)收集。

注:fetchLater() 已在 Chrome 中提供,用于在版本 121(2024 年 1 月發(fā)布)開(kāi)始的原始試驗(yàn)中供真實(shí)用戶測(cè)試,該試驗(yàn)將持續(xù)到 Chrome 126(2024 年 7 月)。

六、聚合

覆蓋率數(shù)據(jù)的來(lái)源是同一版本的代碼,覆蓋率數(shù)據(jù)是可以聚合的,Canyon內(nèi)部使用reportID來(lái)關(guān)聯(lián)測(cè)試用例和細(xì)分聚合維度。這樣做可以讓海量的覆蓋率數(shù)據(jù)聚合成有限個(gè),即Case的數(shù)量。

/**
 * 合并兩個(gè)相同文件的文件覆蓋對(duì)象實(shí)例,確保執(zhí)行計(jì)數(shù)正確。
 *
 * @method mergeFileCoverage
 * @static
 * @param {Object} first 給定文件的第一個(gè)文件覆蓋對(duì)象
 * @param {Object} second 相同文件的第二個(gè)文件覆蓋對(duì)象
 * @return {Object} 合并后的結(jié)果對(duì)象。請(qǐng)注意,輸入對(duì)象不會(huì)被修改。
 */
function mergeFileCoverage(first, second) {
  const ret = JSON.parse(JSON.stringify(first));


  delete ret.l; // 移除派生信息


  Object.keys(second.s).forEach(function (k) {
    ret.s[k] += second.s[k];
  });


  Object.keys(second.f).forEach(function (k) {
    ret.f[k] += second.f[k];
  });


  Object.keys(second.b).forEach(function (k) {
    const retArray = ret.b[k];
    const secondArray = second.b[k];
    for (let i = 0; i < retArray.length; i += 1) {
      retArray[i] += secondArray[i];
    }
  });


  return ret;
}

端到端測(cè)試的覆蓋率數(shù)據(jù)特點(diǎn)之一是單體數(shù)據(jù)體積大,在項(xiàng)目整體插樁的情況下相當(dāng)于整體源代碼體積的30%。攜程Trip.com flight站點(diǎn)的預(yù)定頁(yè)UI自動(dòng)化case上報(bào)次數(shù)每次可達(dá)2000次,每次10M數(shù)據(jù),這樣的數(shù)據(jù)量對(duì)于Canyon服務(wù)端來(lái)說(shuō)是一個(gè)巨大的挑戰(zhàn)。

對(duì)于單條數(shù)據(jù)大且高頻次的數(shù)據(jù)上報(bào)場(chǎng)景,很難做到實(shí)時(shí)數(shù)據(jù)聚合計(jì)算。Canyon采用消息隊(duì)列的形式來(lái)消費(fèi)數(shù)據(jù),并且設(shè)計(jì)成無(wú)狀態(tài)服務(wù),適用于云原生時(shí)代的容器化部署,可通過(guò)HPA彈性伸縮容來(lái)應(yīng)用不同場(chǎng)景下的測(cè)試覆蓋率上報(bào)。

七、報(bào)告

對(duì)于覆蓋率報(bào)告展示,我們沿用了istanbul-report的界面風(fēng)格,但是由于istanbul-report只提供了靜態(tài)html文件的生成,不適合現(xiàn)代化前端水合數(shù)據(jù)生成html的模式,為此我們參考了它的源碼,使用了monaco-editor標(biāo)記源代碼覆蓋率。

const decorations = useMemo(() => {
    if (data) {
        const annotateFunctionsList = annotateFunctions(data.coverage, data.sourcecode);
        const annotateStatementsList = annotateStatements(data.coverage);
        return [...annotateStatementsList, ...annotateFunctionsList].map((i) => {
            return {
                inlineClassName: 'content-class-found',
                startLine: i.startLine,
                startCol: i.startCol,
                endLine: i.endLine,
                endCol: i.endCol,
            };
        });
    } else {
        return [];
    }
}, [data]);

經(jīng)過(guò)著色后的效果:

圖片

八、變更代碼覆蓋率

對(duì)于變更代碼覆蓋率,我們統(tǒng)計(jì)的公式是覆蓋到的新增代碼行/所有新增代碼行。

通過(guò)配置compareTarget來(lái)指定對(duì)比目標(biāo),再聯(lián)合gitlab的git diff接口獲取變更代碼行結(jié)合覆蓋率數(shù)據(jù)計(jì)算。

/**
 * returns computed line coverage from statement coverage.
 * This is a map of hits keyed by line number in the source.
 */
function getLineCoverage(statementMap:{ [key: string]: Range },s:{ [key: string]: number }) {
  const statements = s;
  const lineMap = Object.create(null);


  Object.entries(statements).forEach(([st, count]) => {
    if (!statementMap[st]) {
      return;
    }
    const { line } = statementMap[st].start;
    const prevVal = lineMap[line];
    if (prevVal === undefined || prevVal < count) {
      lineMap[line] = count;
    }
  });
  return lineMap;
}

九、react native 覆蓋率收集方案

攜程的移動(dòng)端技術(shù)棧主要是react native,好消息是對(duì)于我們的插樁方案一樣適用,因?yàn)槎际腔赽abel編譯。并且得力于得力于公司內(nèi)部的react native項(xiàng)目結(jié)構(gòu)統(tǒng)一,我們將編譯時(shí)插樁做到了流水線中,在流水線中分別打包“正常包”和”插樁包“,這樣搭配UI自動(dòng)化可以形成一套完整的錄制回放覆蓋率指標(biāo)收集的測(cè)試體系。

利用websocket暴露模擬器內(nèi)覆蓋率數(shù)據(jù):

// 創(chuàng)建WebSocket連接
const socket = new WebSocket('ws://localhost:8080');


// 當(dāng)WebSocket連接打開(kāi)時(shí)觸發(fā)
socket.onopen = () => {
    console.log('Connected to coverage WebSocket server');
};


// 當(dāng)收到WebSocket消息時(shí)觸發(fā)
socket.onmessage = event => {
    try {
      if (JSON.parse(event.data).type === 'getcoverage') {
        // 發(fā)送覆蓋率數(shù)據(jù)
        socket.send(JSON.stringify(payload));
      }
    } catch (e) {
      console.log(e);
    }
};


// 當(dāng)WebSocket連接關(guān)閉時(shí)觸發(fā)
socket.onclose = () => {
    console.log('Disconnected from coverage WebSocket server');
};

目前攜程機(jī)票部門(mén)的APP模塊均已接入Canyon,經(jīng)過(guò)實(shí)踐istanbuljs可以很好的對(duì)其進(jìn)行插樁及覆蓋率數(shù)據(jù)收集,測(cè)試團(tuán)隊(duì)在每次生產(chǎn)發(fā)布前會(huì)以Canyon的覆蓋率數(shù)據(jù)指標(biāo)來(lái)衡量此次發(fā)布的質(zhì)量情況。

十、覆蓋率提升優(yōu)先級(jí)列表

在用戶最初接入Canyon系統(tǒng)時(shí),會(huì)面臨一個(gè)挑戰(zhàn):如果沒(méi)有大量的UI自動(dòng)化測(cè)試用例,大型應(yīng)用的代碼覆蓋率會(huì)顯得尤為低下。一開(kāi)始,僅僅提供一個(gè)Istanbul代碼覆蓋率報(bào)告,并不能有效指導(dǎo)團(tuán)隊(duì)如何提高覆蓋率,這讓大家感到困惑和無(wú)所適從。

為了解決這個(gè)問(wèn)題,我們進(jìn)行了深入的調(diào)研,并發(fā)現(xiàn)公司已經(jīng)有了一個(gè)成熟的生產(chǎn)環(huán)境代碼覆蓋率收集系統(tǒng)?;谶@一發(fā)現(xiàn),我們決定將這個(gè)系統(tǒng)的數(shù)據(jù)與我們自己的覆蓋率數(shù)據(jù)相結(jié)合,創(chuàng)建了一個(gè)“覆蓋率提升優(yōu)先級(jí)列表”。這個(gè)列表的目的是為開(kāi)發(fā)團(tuán)隊(duì)提供明確的指引,幫助他們了解在哪些方面可以優(yōu)先提升代碼覆蓋率。

為了使這個(gè)指引更加科學(xué)和實(shí)用,我們制定了一個(gè)覆蓋率權(quán)重公式:

生產(chǎn)環(huán)境覆蓋率×100×0.3 + (1 - 測(cè)試覆蓋率)×100×0.3 + 函數(shù)數(shù)量×0.2

通過(guò)這個(gè)公式,我們能夠優(yōu)先識(shí)別出那些生產(chǎn)環(huán)境使用率高、行數(shù)多,測(cè)試覆蓋率低的代碼文件,從而為開(kāi)發(fā)團(tuán)隊(duì)提供針對(duì)性的提升建議。這樣的方法不僅提高了代碼質(zhì)量,也增強(qiáng)了我們對(duì)整體覆蓋率的掌控。

十一、社區(qū)推廣

從這篇文章發(fā)表時(shí)起,我們將正式開(kāi)源Canyon。JavaScript是時(shí)下最流行的編程語(yǔ)言,但是端到端測(cè)試覆蓋率收集領(lǐng)域一直空白,我們的代碼開(kāi)發(fā)基于了istanbuljs,monaco editor等優(yōu)秀開(kāi)源項(xiàng)目,我們有信心推出Canyon開(kāi)源可以贏得社區(qū)的反響,并且可以有大量JavaScript開(kāi)發(fā)者參與進(jìn)來(lái)。

Canyon在未來(lái)還有很大發(fā)展空間,例如生產(chǎn)環(huán)境插樁收集還未有待驗(yàn)證嘗試,與playwright、puppeteer、cypress等自動(dòng)化測(cè)試的工具還沒(méi)有深度鏈接,這些都已經(jīng)規(guī)劃到了未來(lái)的開(kāi)發(fā)計(jì)劃中。希望在未來(lái)Canyon可以在攜程及社區(qū)里有更多人參與建設(shè)。

責(zé)任編輯:張燕妮 來(lái)源: 攜程技術(shù)
相關(guān)推薦

2011-11-01 10:10:48

ScriptCover

2012-04-11 11:21:57

ibmdw

2022-05-31 09:01:18

SwiftApp 項(xiàng)目

2023-10-27 08:49:00

JCovOpenJDK

2021-12-25 22:30:27

Chrome DevTJavaScript調(diào)試工具

2019-09-25 09:20:41

谷歌代碼開(kāi)發(fā)者

2017-01-20 09:45:20

JavaScript代碼質(zhì)量

2021-10-15 13:47:19

覆蓋率檢測(cè) istanbul 總代碼的比例

2011-04-25 09:49:20

代碼測(cè)試

2012-09-21 10:30:56

Linux項(xiàng)目代碼覆蓋率

2015-11-09 17:56:57

WebPHP函數(shù)覆蓋

2016-01-13 10:14:15

WebPHP函數(shù)覆蓋

2022-05-13 09:40:51

代碼可行應(yīng)用性能

2022-10-21 15:29:32

5G網(wǎng)絡(luò)

2025-03-04 00:00:33

2022-03-25 09:22:42

代碼開(kāi)發(fā)

2022-08-25 06:27:39

vivoJaCoCo代碼覆蓋率

2024-09-03 14:10:00

模型測(cè)試

2025-05-08 01:44:00

RussGo調(diào)試

2019-09-30 10:27:52

變異測(cè)試評(píng)估
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)