我有一个非常典型的类似于开关函数,该函数针对给定的输入值(在这种情况下为“身体质量指数”)返回分类符。 (我正在使用此功能,但可以是其他任何性质相同的东西)
现在,它非常像这样:
// ...
const TYPE_SEVERE_THINNESS = -3;
const TYPE_MODERATE_THINNESS = -2;
const TYPE_MILD_THINNESS = -1;
const TYPE_REGULAR = 0;
const TYPE_OVERWEIGHT = 1;
const TYPE_PRE_OBESE = 2;
const TYPE_OBESE_GRADE_I = 3;
const TYPE_OBESE_GRADE_II = 4;
const TYPE_OBESE_GRADE_III = 5;
// ...
public static function classification(float $bmi) : int
{
if ($bmi <= 16.00) {
return self::TYPE_SEVERE_THINNESS;
}
if ($bmi <= 16.99) {
return self::TYPE_MODERATE_THINNESS;
}
if ($bmi <= 18.49) {
return self::TYPE_MILD_THINNESS;
}
if ($bmi <= 24.99) {
return self::TYPE_REGULAR;
}
if ($bmi <= 27.49) {
return self::TYPE_OVERWEIGHT;
}
if ($bmi <= 29.99) {
return self::TYPE_PRE_OBESE;
}
if ($bmi <= 34.99) {
return self::TYPE_OBESE_GRADE_I;
}
if ($bmi <= 39.99) {
return self::TYPE_OBESE_GRADE_II;
}
if ($bmi >= 40) {
return self::TYPE_OBESE_GRADE_III;
}
}
我要进行重构,正在考虑对该功能的所有可能的增强,特别是用于降低C.R.A.P.索引( Change Risk Anti-Patterns ),目前正在返回值110.00
。
当然,可能会有许多可能的增强。随时提出建议。
但是我的问题特别是关于降低cycolmatic复杂性,
a)还有其他方法来构造此代码,以便C.R.A.P.指数走低? b)为了正确测试此功能,我应该生成一个断言每种情况的测试,还是执行许多测试以解决每种可能的情况? (我现在的答案可能是“由您决定”,但是也许存在一种更好的方法来降低圈复杂度,从而让数量较少的测试仍然可以涵盖所有或大多数可能的情况。)
如果必须匹配相等的值,我只会使用一个hashmap(键值数组),但是由于我正在评估范围,因此方法可能会有所不同。
更新:在构建了包含每种方案示例的测试案例后,CRAP索引降至10.01
。不过,我相信还有另一种执行值查找的方法。
/**
* Test it returns a valid WHO classification for BMI type
*
* @return void
*/
public function test_it_returns_a_valid_who_classification_for_bmi_type()
{
// Sample bmi => expected type
// Key must be a string later converted to float
$testMatrix = [
"15" => BMILevel::TYPE_SEVERE_THINNESS,
"16.5" => BMILevel::TYPE_MODERATE_THINNESS,
"18" => BMILevel::TYPE_MILD_THINNESS,
"24" => BMILevel::TYPE_REGULAR,
"27" => BMILevel::TYPE_OVERWEIGHT,
"29" => BMILevel::TYPE_PRE_OBESE,
"34" => BMILevel::TYPE_OBESE_GRADE_I,
"39" => BMILevel::TYPE_OBESE_GRADE_II,
"41" => BMILevel::TYPE_OBESE_GRADE_III,
];
foreach ($testMatrix as $bmi => $categoryCheck) {
$type = BMILevel::classification(floatval($bmi));
$this->assertEquals($type, $categoryCheck);
}
}
答案 0 :(得分:1)
测试通常可以让您反映现有的(也许是过时的)实现:
const TYPE_SEVERE_THINNESS = -3;
const TYPE_MODERATE_THINNESS = -2;
const TYPE_MILD_THINNESS = -1;
const TYPE_REGULAR = 0;
const TYPE_OVERWEIGHT = 1;
const TYPE_PRE_OBESE = 2;
const TYPE_OBESE_GRADE_I = 3;
const TYPE_OBESE_GRADE_II = 4;
const TYPE_OBESE_GRADE_III = 5;
所以您在这里是一个有序列表(从上到下):
const TYPES = [-3, -2, -1, 0, 1, 2, 3, 4, 5];
或者也许:
const TYPES = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const TYPE_REFERENCE = 4;
和相应的值(不是键,不是转换的问题):
const VALUES = [15, 16.5, 18, 24, 27, 29, 34, 39, 41];
,您可能要提供标签:
const LABELS = ["Severe Thinness", "Moderate Thinness", "Mild Thinness",
"Regular", "Overweight", "Pre-Obese", "Obese Grade I",
"Obese Grade II", "Obese Grade III"];
因此,可以轻松想象的是,大多数不属于代码,而是属于用于配置代码的数据。然后,单元测试可以针对不同的数据集进行测试,这不仅可以提高被测系统的稳定性,而且可以提高系统的稳定性。 测试哪些扩展名(更改)可以很容易地应用现有代码。
在测试中使用数据提供者通常会表明,代码库本身最有可能应该具有一些数据提供者,而不是那么硬编码。
答案 1 :(得分:0)
好的,我设法获得了相当合理的C.R.A.P.重构后索引,同时保持测试绿色。
我将函数转换为具有上限(自下而上)的查找。我需要为超出范围的值添加一个额外的大小写,并覆盖该大小写。
代码:
public static function classification(float $bmi) : int
{
$classifications = [
['limit' => 16.0 , 'type' => self::TYPE_SEVERE_THINNESS],
['limit' => 16.99, 'type' => self::TYPE_MODERATE_THINNESS],
['limit' => 18.49, 'type' => self::TYPE_MILD_THINNESS],
['limit' => 24.99, 'type' => self::TYPE_REGULAR],
['limit' => 27.49, 'type' => self::TYPE_OVERWEIGHT],
['limit' => 29.99, 'type' => self::TYPE_PRE_OBESE],
['limit' => 34.99, 'type' => self::TYPE_OBESE_GRADE_I],
['limit' => 39.99, 'type' => self::TYPE_OBESE_GRADE_II],
['limit' => 60 , 'type' => self::TYPE_OBESE_GRADE_III],
];
foreach ($classifications as $classification) {
if ($bmi <= $classification['limit']) {
return $classification['type'];
}
}
return self::TYPE_OBESE_GRADE_III;
}
测试:
/**
* Test it returns a valid WHO classification for BMI type
*
* @return void
*/
public function test_it_returns_a_valid_who_classification_for_bmi_type()
{
// Sample bmi => expected type
// Key must be a string later converted to float
$testMatrix = [
"15" => BMILevel::TYPE_SEVERE_THINNESS,
"16.5" => BMILevel::TYPE_MODERATE_THINNESS,
"18" => BMILevel::TYPE_MILD_THINNESS,
"24" => BMILevel::TYPE_REGULAR,
"27" => BMILevel::TYPE_OVERWEIGHT,
"29" => BMILevel::TYPE_PRE_OBESE,
"34" => BMILevel::TYPE_OBESE_GRADE_I,
"39" => BMILevel::TYPE_OBESE_GRADE_II,
"41" => BMILevel::TYPE_OBESE_GRADE_III,
"100" => BMILevel::TYPE_OBESE_GRADE_III, // After upper bound limit
];
foreach ($testMatrix as $bmi => $categoryCheck) {
$type = BMILevel::classification(floatval($bmi));
$this->assertEquals($type, $categoryCheck);
}
}
仍然欢迎有关如何增强功能的提示。