在 重构:改善既有代码的设计 (第2版) 中,构筑测试体系 – 待测试的示例代码的编写

1、一份有待测试的代码。这份代码来自一个简单的应用,用于支持用户查看并调整生产计划。它的(略显粗糙的)界面长得像下面这张图所示的这样。如图1

图1

2、示例代码最终实现如下,入口的 productionPlan.html 文件代码如下

<!DOCTYPE html>
<html lang="en-US">
<head>
    <meta charset="utf-8">
    <title>构筑测试体系 - 待测试的示例代码</title>
    <style>
    </style>
    <script type="module" src="productionPlan.js"></script>
</head>
<body>

</body>
</html>

3、引入的模块 productionPlan.js 文件代码如下

function sampleProvinceData() {
    return {
        name: "Asia",
        producers: [
            {name: "Byzantium", cost: 10, production: 9},
            {name: "Attalia", cost: 12, production: 10},
            {name: "Sinope", cost: 10, production: 6},
        ],
        demand: 30,
        price: 20
    };
}

class Province {
    constructor(doc) {
        this._name = doc.name;
        this._producers = [];
        this._totalProduction = 0;
        this._demand = doc.demand;
        this._price = doc.price;
        doc.producers.forEach(d => this.addProducer(new Producer(this, d)));
    }

    addProducer(arg) {
        this._producers.push(arg);
        this._totalProduction += arg.production;
    }

    get name() {
        return this._name;
    }

    get producers() {
        return this._producers.slice();
    }

    get totalProduction() {
        return this._totalProduction;
    }

    set totalProduction(arg) {
        this._totalProduction = arg;
    }

    get demand() {
        return this._demand;
    }

    set demand(arg) {
        this._demand = parseInt(arg);
    }

    get price() {
        return this._price;
    }

    set price(arg) {
        this._price = parseInt(arg);
    }

    get shortfall() {
        return this._demand - this.totalProduction;
    }

    get profit() {
        return this.demandValue - this.demandCost;
    }

    get demandCost() {
        let remainingDemand = this.demand;
        let result = 0;
        this.producers
            .sort((a, b) => a.cost - b.cost)
            .forEach(p => {
                const contribution = Math.min(remainingDemand, p.production);
                remainingDemand -= contribution;
                result += contribution * p.cost;
            });
        return result;
    }

    get demandValue() {
        return this.satisfiedDemand * this.price;
    }

    get satisfiedDemand() {
        return Math.min(this._demand, this.totalProduction);
    }
}

class Producer {
    constructor(aProvince, data) {
        this._province = aProvince;
        this._cost = data.cost;
        this._name = data.name;
        this._production = data.production || 0;
    }

    get name() {
        return this._name;
    }

    get cost() {
        return this._cost;
    }

    set cost(arg) {
        this._cost = parseInt(arg);
    }

    get production() {
        return this._production;
    }

    set production(amountStr) {
        const amount = parseInt(amountStr);
        const newProduction = Number.isNaN(amount) ? 0 : amount;
        this._province.totalProduction += newProduction - this._production;
        this._production = newProduction;
    }
}

const asia = new Province(sampleProvinceData());
console.log(asia.shortfall);

3、代码 const asia = new Province(sampleProvinceData()); 参考:4.3 第一个测试示例。如图2

图2

4、在浏览器中打开:http://localhost/refactoring/4/productionPlan.html ,查看控制台输出:5,符合预期。如图3

图3

永夜

View Comments