7

考虑以下伪代码。它旨在确定一个成绩是否为及格成绩。

class Student:
    int grade

    boolean IsStudentPassing():
        return grade >= MIN_PASSING_GRADE


...

// In another file
constant int MIN_PASSING_GRADE = 70

如果我们正在为 编写单元测试IsStudentPassing,我们可以使用常量值:

ensure that IsStudentPassing is false when grade is MIN_PASSING_GRADE - 1
ensure that IsStudentPassing is true when grade is MIN_PASSING_GRADE

或者,我们可以手动选择值:

ensure that IsStudentPassing is false when grade is 69
ensure that IsStudentPassing is true when grade is 70

对于第二种方法,如果发生更改,我们的测试必须重新编写MIN_PASSING_GRADE。第一种方法更灵活,但依赖于MIN_PASSING_GRADE具有正确的值。

我不完全确定更喜欢哪种方法,一般会根据具体情况进行选择。一方面,MIN_PASSING_GRADE应该通过不同的测试来确保它是理智的。另一方面,我担心所谓的“单元”测试会触及代码库中的太多其他地方。

这是一个人为的例子,但在实际程序中经常会出现类似的情况。解决它们的最佳方法是什么?

4

3 回答 3

1

根据偏好,您可以在自己的设计中注入“恒定”值,这样您的单元测试就可以与实际上构成及格分数的变幻莫测隔离开来。做到这一点的难易程度因编程语言而异。考虑一下这个代码,让它变得简单:

use MooseX::Declare;
class Student {
    has grade => (
        is => 'ro', isa => 'Num', required => 1,
    );

    method min_passing_grade {
        return MIN_PASSING_GRADE;
    )

    method is_student_passing () {
        return $self->grade >= $self->min_passing_grade
    }
}

class t::Student {
    use Test::Sweet;
    use Test::MockObject::Extends;

    test correctly_determines_student_is_passing () {
        my $student = $self->_make_student($self->_passing_grade);

        ok($student->is_student_passing);
    }

    method _make_student (Num $grade) {
        my $student = Test::MockObject::Extends->new(
            $student->new(grade => $grade)
        );
        # Here's the important line:
        $student->set_always(
            min_passing_grade => $self->_passing_grade
        );
        return $student;
    }

    method _passing_grade () { 40 }

    test correctly_determines_student_is_failing () {
        my $student = $self->_make_student($self->_passing_grade - 1);

        ok(not $student->is_student_passing);
    }
}

现在,这就是 Perl,它使猴子补丁变得非常简单(上面的“重要行”替换了Student::min_passing_grade运行时的实现)。您还可以将 questino 中的值设置为默认为常量的属性,甚至提供常量文件的特殊版本以供单元测试使用。

在没有真正强大的性能要求的情况下,我将选择上述方法,而不是让值成为一个真正的常数。只有当我找不到从单元测试中注入我想要的值的方法时,我才会使用常用定义的常量。我认为在几乎任何情况下你都不应该做的是在这个测试中复制常量,毕竟这是为了确保逻辑Student正确的。

于 2012-09-10T20:36:02.667 回答
1

首选是根本不使用常量,而是使其成为传递的接口的属性。这就是依赖注入的工作原理(这反过来又有助于 TDD)。

然后,您将使用测试(或模拟)框架来生成测试。注意,您可能希望在目标值的任一侧测试多个值。您还需要测试数据类型的边界以捕获上溢/下溢错误。

于 2012-09-12T14:36:43.223 回答
0

正如我过去一直试图操作的那样:单​​元测试应该检查所有可能的边界条件/压力测试特定功能或整个功能的子集,并且更大的回归套件应该测试整个程序的业务需求有效性边。

因此,在您的示例中,第一个测试将在我的单元测试中,第二个在我的回归中。

然而,我是这个行业的新手,我很想知道我是否走在正确的轨道上。

于 2012-09-10T18:17:29.233 回答