2

我想重构我的模型,以便为它正确编写单元测试。但我有一些依赖。谁能指出我如何删除这些依赖项的正确方向?

class Customer
{
    public function save(array $data, $id = NULL)
    {
        // create or update
        if (empty($id)) {
            $customer = new \Entities\Customer();
        } else {
            $customer = $this->findById((int) $id);
        }

        $birthday = new DateTime();
        list($day, $month, $year) = explode("/", $data['birthday']);
        $birthday->setDate($year, $month, $day);

        $customer->setFirstName($data['firstName']);
        $customer->setLastName($data['lastName']);
        $customer->setCompany($data['company']);
        $languageModel = new Application_Model_Language();
        $customer->setLanguage($languageModel->findById($data['language']));
        $resellerShopModel = new Application_Model_ResellerShop();
        $customer->setResellerShop($resellerShopModel->findById($data['resellerShop']));
        $customer->setEmail($data['email']);
        $customer->setPhone($data['phone']);
        $customer->setGender($data['gender']);
        $customer->setBirthday($birthday);
        $customer->setType($data['type']);
        $customerSectorModel = new Application_Model_CustomerSector();
        $customer->setSector($customerSectorModel->findById($data['sector']));
        $customerReferenceModel = new Application_Model_CustomerReference();
        $customer->setReference($customerReferenceModel->findById($data['reference']));
        if (isset($data['password'])) {
            $customer->setPassword($this->_hashPassword($data['password']));
        }

        return $customer;
    }
}
4

2 回答 2

2

我猜你的依赖是函数体中的构造函数调用。在我看来,有 3 种方法可以在单元测试中替换它们:

  1. 创建一个模拟库,其中实现了所有不同的类,并运行测试包括模拟库而不是真正的模块。

  2. 将一组外部类的默认参数添加到您的函数声明中。最后,您的新函数声明将如下所示public function save(array $data, $id = NULL, $newCustomer=\Entities\Customer(), $newLangModel = Application_Model_Language, ...)。同样在函数体中,您可以使用变量来创建实际对象,例如$customer = new $newCustomer(). 在测试代​​码中,您可以通过模拟类覆盖每个依赖的类。

  3. 您不会为每个类添加参数,而是创建两个工厂:一个创建当前对象,一个创建模拟对象。在函数内部,您只能从工厂请求新对象。

    如果有许多不同的地方应该拦截构造,这种方法很有用。如果只有一个功能需要改变,那么工厂就是过度设计。

于 2010-09-29T14:10:34.390 回答
0

回顾您的代码后,该类似乎既是工厂又是 \Entities\Customer 类的存储库(这并不明显,因此您可以考虑重命名以更明确地表达您的意图)。我也不同意将该函数命名为 save ,因为它只返回一个需要持久化的对象,但这更符合语义。

使用您现在拥有的代码,我看到需要两个不同的测试。

1) 测试您的 \Entities\Customer 类。

验证您的每个 getter 和 setter 是否正常工作,或者至少验证其背后有业务逻辑的任何一个。例如,确保如果您设置了 ResellerShop,那么如果您获得了正确/有效的 ResellerShow 对象。或多或少,您需要测试您的业务逻辑。获取/设置基本字段(例如名称)并不需要他们自己的测试(除非 100% 的代码覆盖率是一个目标,我认为不是)。

2)测试(Factory\Repository)类。

确保您显示的类根据传递到数组中的数据正确创建(或失败)。例如,如果未传入必填字段,它应该会失败,并且它不应该返回客户实体对象。

这个想法是为了分离关注点。您的 Entity 类应该包含您的业务逻辑,并且应该与您的对象实例化代码分开进行测试。

于 2010-09-29T20:53:28.327 回答