如何在函数调用后计算作为函数参数给出的强制表达式?

时间:2015-09-30 20:40:17

标签: php testing

我正在编写自己的测试类。我遇到的问题是测试正在测试的函数是否会抛出预期的异常。

我知道我可以这样做:

try{
  tested_function($expression->beingTestedThatWillThrowAnException());
}catch ($exception){
   if($exception instanceof MyExpectedException){
     echo 'OK';
   } else {
     echo 'FAILED';
   }
}

但是我希望我不必每次都写这个try ... catch块,所以我想把它放在测试器类方法中。

但是当我做这样的事情时

class Tester {
   /**
    * @param mixed expression to evaluate
    * @param string expected exception class name
    */
   public function assertException($expression, $expectedException){
     try{
       $expression;
     } catch ($ex) {
       if(is_subclass_of($ex, $expectedException)){
         echo 'OK';
       } else {
         echo 'FAILED';
       }
     }

这失败了,因为$expression在方法调用时被评估,所以在程序进入try块之前。

我尝试的另一种方法是使用eval并将$expression作为字符串传递:

class Tester {
   /**
    * @param string expression to evaluate
    * @param string expected exception class name
    */
   public function assertException($expression, $expectedException){
     try{
       eval($expression);
     } catch ($ex) {
       if(is_subclass_of($ex, $expectedException)){
         echo 'OK';
       } else {
         echo 'FAILED';
       }
     }

这没关系,但是它不允许我使用主范围内的变量,所以例如这一行失败$test->assertException('$d->divideBy(0);');因为$d中没有Tester::assertException()变量范围。

我应该将所有可能的变量名称声明为全局吗?

如何强制在方法中评估表达式(或以其他方式实现所需的结果)?

我知道有现成的单元测试人员(PHPUnit,SimpleTest等),但我自己也希望能够做到这一点。

2 个答案:

答案 0 :(得分:1)

您可以将匿名函数(闭包)作为$ expression传递,并使用use关键字将任何变量绑定到它 - http://php.net/manual/en/functions.anonymous.php

抱歉英语不好,我希望这是可以理解的。

答案 1 :(得分:1)

这不是对您的问题的直接回应,而是实现预期结果的另一种方式。

PHPUnit有一种通过使用注释来声明异常的有趣方法。我做了一个小概念证明,说明如何通过注释来完成。

我们的单元测试小框架:

/**
 * for now, it is added just for Dependency Inversion Principle
 */
interface Test {}

class TestRunner
{
    /**
     * This method will run our tests
     * 
     * @param array $tests
     */
    static public function run(Test $testObject)
    {
        $reflectedClass = new ReflectionClass($testObject);
        foreach ($reflectedClass->getMethods() as $reflectedMethod) {
            // we test only the methods which start with 'test'
            if (strpos($reflectedMethod->getName(), 'test') === 0) {
                $docComment = $reflectedMethod->getDocComment();

                try {
                    // call our method
                    $reflectedMethod->invoke($testObject);
                } catch (Exception $exception) {
                    $expectedExceptions = preg_match_all(
                        '/(\*\s@assertException)(\s+)(?<exception_class>[^\s]*)/',
                        $docComment,
                        $matches
                    );

                    if ($matches) {
                        $status = 'FAILED';
                        if (in_array(get_class($exception), $matches['exception_class'])) {
                            $status = 'OK';
                        }

                        printf("test '%s' status: %s\n", $reflectedMethod->getName(), $status);
                    } else {
                        throw $exception;
                    }
                }
            }
        }
    }
}

和我们的测试:

class MyCustomException extends Exception {}

class MyTest implements Test
{
    /**
     * @assertException MyCustomException
     */
    public function testMe()
    {
        // replace this with your expression
        // which should trow the exception
        throw new MyCustomException();
    }
}

TestRunner::run(new MyTest());