国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

單元測(cè)試:如何編寫可測(cè)試的代碼?

這篇具有很好參考價(jià)值的文章主要介紹了單元測(cè)試:如何編寫可測(cè)試的代碼?。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

在編寫代碼的時(shí)候,大部分時(shí)間想的都是如何實(shí)現(xiàn)功能,很少會(huì)考慮到代碼的可測(cè)試性。

又因?yàn)榇蟛糠止緵](méi)有要求寫單元測(cè)試,完成的功能都是通過(guò)服務(wù)模擬的方式測(cè)試,更加不會(huì)考慮代碼的可測(cè)試性了。

常見(jiàn)的可測(cè)試性不好的代碼,幾種情況(取自極客時(shí)間王錚設(shè)計(jì)模式之美)

  • 未決行為,例如時(shí)間、隨機(jī)數(shù)等
  • 全局變量,要考慮用例的執(zhí)行順序,或者有些mock框架是并發(fā)執(zhí)行的
  • 靜態(tài)方法,比如耗時(shí)長(zhǎng),依賴外部資源、邏輯復(fù)雜、行為未決時(shí),需要進(jìn)行模擬
  • 復(fù)雜繼承
  • 高度耦合

單元測(cè)試編寫過(guò)程中,經(jīng)常會(huì)遇到下面幾類問(wèn)題。實(shí)際上是由于編寫的代碼可測(cè)試性差導(dǎo)致的。

1、單元測(cè)試時(shí),維護(hù)一個(gè)第三方的服務(wù),而且需要按照需要返回各種結(jié)果(成功的、失敗、異常),成本是比較高的。如果第三方程序不是自己維護(hù)的,想要做到,更是不可能的。

? ? ? 解決方案:新增一個(gè)serviceEx類,繼承正常運(yùn)行時(shí)調(diào)用的service類,然后在serviceEx中重寫方法,模擬自己想要的結(jié)果,供單元測(cè)試用例使用。而運(yùn)行的程序仍舊使用service類。

2、第三方的類,比如RedisDistributeLock這種類似工具類的鎖,要想確定其返回鎖成功或者失敗,也是很難做到的。

3、一些未決定行為,比如隨機(jī)數(shù)、當(dāng)前時(shí)間System.currentTimeMillis(),因其不確定性,在運(yùn)行單元測(cè)試時(shí),會(huì)導(dǎo)致結(jié)果不可控

原則:就是把不確定、調(diào)用不通的內(nèi)容進(jìn)行封裝然后通過(guò)繼承、重寫等方式,把封裝的內(nèi)容進(jìn)行替換,直接返回自己需要的內(nèi)容。

下面是示例代碼,解決以上三類問(wèn)題,僅供參考

想運(yùn)行示例可直接下載代碼,免費(fèi)https://download.csdn.net/download/zhaoronghui1314/86765041

不可測(cè)試代碼示例

package com.zrh.jsd.temp;

import javax.transaction.InvalidTransactionException;
import java.util.UUID;

public class Transaction {
    private String id;
    private Long buyerId;
    private Long createTimestamp;
    private int status;
    private String walletTransactionId;

    public Transaction(String preAssignedId, Long buyerId) {
        if (preAssignedId != null && !preAssignedId.isEmpty()) {
            this.id = preAssignedId;
        } else {
            this.id = UUID.randomUUID().toString();
        }
        if (!this.id.startsWith("t_")) {
            this.id = "t_" + preAssignedId;
        }
        this.buyerId = buyerId;
        this.status = STATUS.TO_BE_EXECUTD;
        this.createTimestamp = System.currentTimeMillis();
    }

    public boolean execute() throws InvalidTransactionException {
        if (buyerId == null) {
            throw new InvalidTransactionException();
        }
        if (status == STATUS.EXECUTED) {
            return true;
        }
        boolean isLocked = false;
        try {
            // 修改點(diǎn)1:可以理解為第三方類,運(yùn)行單元測(cè)試時(shí),需要的lock狀態(tài)不方便得到
            // 僅做示例,此代碼不可運(yùn)行。按下方修改后可運(yùn)行。
            isLocked = RedisDistributedLock.getSingletonIntance().lockTransction(id);
            if (!isLocked) {
                return false;
            }
            if (status == STATUS.EXECUTED) {
                return true;
            } ;
            // 修改點(diǎn)2:當(dāng)前時(shí)間未決定的,運(yùn)行單元測(cè)試時(shí),此處不可控。
            long executionInvokedTimestamp = System.currentTimeMillis();
            if (executionInvokedTimestamp - createTimestamp > 14) {
                this.status = STATUS.EXPIRED;
                return false;
            }
            // 修改點(diǎn)3:WalletRpcService是第三方服務(wù),運(yùn)行單元測(cè)試時(shí)不一定可以正常調(diào)用
            WalletRpcService walletRpcService = new WalletRpcService();
            String walletTransactionId = walletRpcService.moveMoney();
            if (walletTransactionId != null) {
                this.walletTransactionId = walletTransactionId;
                this.status = STATUS.EXECUTED;
                return true;
            } else {
                this.status = STATUS.FAILED;
                return false;
            }
        } finally {
            if (isLocked) {
                // 僅做示例,此代碼不可運(yùn)行。按下方修改后可運(yùn)行。
                RedisDistributedLock.getSingletonIntance().unlockTransction(id);
            }
        }
    }
}
package com.zrh.jsd.temp;

public class WalletRpcService {
    public String moveMoney() {
        System.out.println("這里是WalletRpcService第三方服務(wù)");
        return "asb";
    }
}
package com.zrh.jsd.temp;

public class STATUS {
    static final int TO_BE_EXECUTD = 0;

    static final int EXECUTED = 1;

    static final int EXPIRED = 2;

    static final int FAILED = 3;

}

優(yōu)化之后可測(cè)試的代碼,包含單元測(cè)試用例

package org.example;

import javax.transaction.InvalidTransactionException;
import java.util.UUID;

public class Transaction {
    private String id;
    private Long buyerId;
    private Long createTimestamp;
    private int status;
    private String walletTransactionId;

    // 添加一個(gè)成員變量及其 set 方法。就可以將對(duì)象放到外面
    private WalletRpcService walletRpcService;

    private TransactionLock lock;
    // 修改點(diǎn)2,提出方法,在test類中重寫此方法。
    protected boolean isExpired() {
        long executionInvokedTimestamp = System.currentTimeMillis();
        System.out.println("=======方法內(nèi)部的isExpired==");
        return executionInvokedTimestamp - createTimestamp > 14;
    }

    public void setTransactionLock(TransactionLock lock) {
        this.lock = lock;
    }
    // 修改點(diǎn)3:WalletRpcService改為注入的方式,通過(guò)構(gòu)造傳入,避免在類中new
    public Transaction(String preAssignedId, Long buyerId, WalletRpcService walletRpcService) {
        if (preAssignedId != null && !preAssignedId.isEmpty()) {
            this.id = preAssignedId;
        } else {
            this.id = UUID.randomUUID().toString();
        }
        if (!this.id.startsWith("t_")) {
            this.id = "t_" + preAssignedId;
        }
        this.buyerId = buyerId;
        this.status = STATUS.TO_BE_EXECUTD;
        this.createTimestamp = System.currentTimeMillis();
        this.walletRpcService = walletRpcService;
    }

    public boolean execute() throws InvalidTransactionException {
        if (buyerId == null) {
            throw new InvalidTransactionException();
        }
        if (status == STATUS.EXECUTED) {
            return true;
        }
        boolean isLocked = false;
        try {
            isLocked = lock.lock(id);
            if (!isLocked) {
                return false; // 鎖定未成功,返回 false,job 兜底執(zhí)行
            }
            if (status == STATUS.EXECUTED) {
                return true;
            }
            // createTimestamp 臨時(shí)
            if (isExpired()) {
                this.status = STATUS.EXPIRED;
                return false;
            }

            String walletTransactionId = walletRpcService.moveMoney();
            if (walletTransactionId != null) {
                this.walletTransactionId = walletTransactionId;
                this.status = STATUS.EXECUTED;
                return true;
            } else {
                this.status = STATUS.FAILED;
                return false;
            }
        } finally {
            if (isLocked) {
                lock.unlock(id);
            }
        }
    }
}
package org.example;

public class MockWalletRpcServiceOne extends WalletRpcService {
    @Override
    public String moveMoney() {
        System.out.println("這里是WalletRpcService模擬服務(wù)");
        return "asb";
    }
}
package org.example;

public class RedisDistributedLock {
    public static RedisDistributedLock getSingletonIntance() {
        return new RedisDistributedLock();
    }

    boolean lockTransction(String id) {
        return true;
    }

    boolean unlockTransction(String id) {
        return true;
    }
}
package org.example;

public class STATUS {
    static final int TO_BE_EXECUTD = 0;

    static final int EXECUTED = 1;

    static final int EXPIRED = 2;

    static final int FAILED = 3;

}
package org.example;

public class TransactionLock {
    public boolean lock(String id) {
        return RedisDistributedLock.getSingletonIntance().lockTransction(id);
    }

    public boolean unlock(String id) {
        return RedisDistributedLock.getSingletonIntance().unlockTransction(id);
    }
}
package org.example;

public class WalletRpcService {
    public String moveMoney() {
        System.out.println("這里是WalletRpcService第三方服務(wù)");
        return "asb";
    }
}

單元測(cè)試用例文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-479025.html

package org.example;

import org.junit.jupiter.api.Test;

import javax.transaction.InvalidTransactionException;

import static org.junit.jupiter.api.Assertions.assertTrue;

public class TransactionTest {
    @Test
    public void testExecute() throws InvalidTransactionException {
        Long buyerId = 123L;
        // 修改點(diǎn)3:出入service,重寫service中的方法,直接返回模擬結(jié)果,不依賴第三方服務(wù)
        WalletRpcService walletRpcService = new MockWalletRpcServiceOne();
        // 修改點(diǎn)2:模擬lock,重寫方法,模擬返回的結(jié)果
        TransactionLock mockLock = new TransactionLock() {
            public boolean lock(String id) {
                System.out.println("這里是模擬的lock");
                return true;
            }

            public boolean unlock(String id) {
                System.out.println("這里是模擬的unlock");
                return true;
            }
        };
        // walletRpcService 可以通過(guò)構(gòu)造方法注入或者通過(guò)set方法注入
        Transaction transaction = new Transaction(null, buyerId, walletRpcService) {
            // 這里必須是protect以上的級(jí)別。private不可
            // 修改點(diǎn)1:重寫isExpired,返回期望的內(nèi)容
            protected boolean isExpired() {
                System.out.println("這里是外部的isExpired方法");
                return false;
            }
        };
        transaction.setTransactionLock(mockLock);
        boolean executedResult = transaction.execute();
        assertTrue(executedResult);
    }
}

到了這里,關(guān)于單元測(cè)試:如何編寫可測(cè)試的代碼?的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 如何讓chatgpt十分正確的幫咱們編寫代碼文檔和單元測(cè)試

    ? 有多少次你專注于編程而忘記了寫函數(shù)、方法、類的非常簡(jiǎn)單的代碼文檔?我不是在問(wèn)單元測(cè)試.?直到我發(fā)現(xiàn)ChatGPT可以做到這一點(diǎn): 除了代碼文檔,它在編寫單元測(cè)試方面也做得很好。此外,在最后,我可以要求他為其他想使用我的代碼的貢獻(xiàn)者生成一個(gè)用戶友好的文檔。

    2024年02月15日
    瀏覽(21)
  • 04 單元測(cè)試:怎樣提升最小可測(cè)試單元的質(zhì)量?

    04 單元測(cè)試:怎樣提升最小可測(cè)試單元的質(zhì)量?

    上一篇文章“03 微服務(wù)架構(gòu)下的測(cè)試策略” 我講到了**微服務(wù)架構(gòu)下的測(cè)試策略和質(zhì)量保障體系**,今天我來(lái)講講測(cè)試策略中的最底層測(cè)試——單元測(cè)試。 單元測(cè)試的價(jià)值 單元測(cè)試是一種白盒測(cè)試技術(shù),通常由開(kāi)發(fā)人員在編碼階段完成,目的是驗(yàn)證軟件代碼中的每個(gè)單元(方

    2024年01月17日
    瀏覽(22)
  • 軟件測(cè)試中如何編寫單元測(cè)試用例(白盒測(cè)試)

    目錄 前言: 一、 單元測(cè)試的概念 二、開(kāi)始測(cè)試前的準(zhǔn)備 三、開(kāi)始測(cè)試 四、完成測(cè)試 前言: 單元測(cè)試是軟件測(cè)試中一種重要的測(cè)試方法,它是在代碼級(jí)別進(jìn)行測(cè)試,通過(guò)對(duì)每個(gè)模塊或功能進(jìn)行獨(dú)立測(cè)試來(lái)保障代碼的正確性和可靠性。單元測(cè)試可以有效地避免產(chǎn)生隱藏的代

    2024年02月09日
    瀏覽(20)
  • Spring Boot中如何編寫優(yōu)雅的單元測(cè)試

    單元測(cè)試是指對(duì)軟件中的最小可測(cè)試單元進(jìn)行檢查和驗(yàn)證。在Java中,單元測(cè)試的最小單元是類。通過(guò)編寫針對(duì)類或方法的小段代碼,來(lái)檢驗(yàn)被測(cè)代碼是否符合預(yù)期結(jié)果或行為。執(zhí)行單元測(cè)試可以幫助開(kāi)發(fā)者驗(yàn)證代碼是否正確實(shí)現(xiàn)了功能需求,以及是否能夠適應(yīng)應(yīng)用環(huán)境或需求

    2024年02月11日
    瀏覽(24)
  • 如何創(chuàng)建自己的Spring Boot Starter并為其編寫單元測(cè)試

    當(dāng)我們想要封裝一些自定義功能給別人使用的時(shí)候,創(chuàng)建Spring Boot Starter的形式是最好的實(shí)現(xiàn)方式。如果您還不會(huì)構(gòu)建自己的Spring Boot Starter的話,本文將帶你一起創(chuàng)建一個(gè)自己的Spring Boot Starter。 創(chuàng)建一個(gè)新的 Maven 項(xiàng)目。第三方封裝的命名格式是 xxx-spring-boot-starter ,例如:

    2024年03月15日
    瀏覽(26)
  • Service層代碼單元測(cè)試以及單元測(cè)試如何Mock

    Service層代碼單元測(cè)試以及單元測(cè)試如何Mock

    接著上一篇文章:?jiǎn)卧獪y(cè)試入門篇,本篇文章作為單元測(cè)試的進(jìn)階篇,主要介紹如何對(duì)Springboot Service層代碼做單元測(cè)試,以及單元測(cè)試中涉及外調(diào)服務(wù)時(shí),如何通過(guò)Mock完成測(cè)試。 現(xiàn)在項(xiàng)目都流行前后端代碼分離,后端使用springboot框架,在service層編寫接口代碼實(shí)現(xiàn)邏輯。假設(shè)

    2023年04月08日
    瀏覽(18)
  • 單元測(cè)試的哲學(xué):如何確保代碼質(zhì)量

    單元測(cè)試是軟件開(kāi)發(fā)過(guò)程中的一種重要方法,它的目的是通過(guò)對(duì)代碼的自動(dòng)化測(cè)試來(lái)確保其正確性和可靠性。在過(guò)去的幾十年里,單元測(cè)試逐漸成為軟件開(kāi)發(fā)的標(biāo)準(zhǔn)做法,并且在各種規(guī)模的項(xiàng)目中得到了廣泛應(yīng)用。然而,在實(shí)踐中,很多開(kāi)發(fā)人員并沒(méi)有充分利用單元測(cè)試的潛

    2024年02月20日
    瀏覽(24)
  • 記單元測(cè)試的時(shí)候Mockito RedisTemplate的時(shí)候 報(bào)setIfAbsent null

    mock方法這樣寫 RedisTemplateString, Object redisTemplate = mock(RedisTemplate.class); when(mockRedisUtils.getRedisTemplate()).thenReturn(redisTemplate); ValueOperationsString, Object valueOperations = mock(ValueOperations.class); when(redisTemplate.opsForValue()).thenReturn(valueOperations); when(valueOperations.setIfAbsent(any(String.class), any(String

    2024年02月12日
    瀏覽(17)
  • 【單元測(cè)試】--編寫單元測(cè)試

    一、編寫第一個(gè)單元測(cè)試 編寫第一個(gè)單元測(cè)試通常包括以下步驟。以下示例以C#和NUnit為例: 創(chuàng)建測(cè)試項(xiàng)目 : 在Visual Studio中,創(chuàng)建一個(gè)新的Class Library項(xiàng)目,這將是你的單元測(cè)試項(xiàng)目。 在解決方案資源管理器中,右鍵點(diǎn)擊項(xiàng)目,選擇 “管理 NuGet 包”,然后搜索并安裝NUnit框

    2024年02月07日
    瀏覽(21)
  • 單元測(cè)試:優(yōu)雅編寫Kotlin單元測(cè)試

    單元測(cè)試:優(yōu)雅編寫Kotlin單元測(cè)試

    一、MockK簡(jiǎn)介 MockK是一款功能強(qiáng)大、易于使用的Kotlin mocking框架。在編寫 單元測(cè)試 時(shí),MockK能夠幫助我們簡(jiǎn)化代碼、提高測(cè)試覆蓋率,并改善測(cè)試的可維護(hù)性。除了基本用法外,MockK還提供了許多額外的功能和靈活的用法,讓我們能夠更好地模擬對(duì)象行為、驗(yàn)證函數(shù)調(diào)用,并在

    2024年02月10日
    瀏覽(19)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包