我开始在这个项目中使用单元和功能测试,因此我有一些问题:
我正在使用symfony php框架。我有像LDAP ORM服务这样的学说。
此外,我有一个用户存储库(作为服务),它依赖于LDAP ORM服务,记录器和验证服务。
现在我想为UserRepo的addUser函数编写单元测试。 它将在内部调用:getNewUidNumber,userToEntities,doesUserExist和getUserByUid。
我的问题是: 我应该模拟所有这些内部函数来测试addUser函数吗?这是否违背了单元测试的想法(只是测试API)。
或者我应该只是模拟LDAP ORM服务,Logger和验证服务,以便该类调用所有内部函数?但是这会导致一个巨大的测试功能,因为我必须模拟所有内部存储库调用的存储库。
或者我应该启动symfony内核并使用ServiceContainer将ORM LDAP服务与真实的测试数据库一起使用。但这不是功能测试而不是单元测试吗? 我听说在测试中有这么多依赖项是不好的。所以我认为使用整个serviceContainer会很糟糕。
增加会员:
public function addUser(User $user)
{
$pbnlAccount = $this->userToEntities($user);
if(!$this->doesUserExist($user)) {
$pbnlAccount->setUidNumber($this->getNewUidNumber());
$this->ldapEntityManager->persist($pbnlAccount);
$this->ldapEntityManager->flush();
}
else {
throw new UserAlreadyExistException("The user ".$user->getUid()." already exists.");
}
return $this->getUserByUid($user->getUid());
}
更多代码,例如内部函数: https://gist.github.com/NKPmedia/4a6ee55b6bb96e8af409debd98950678
由于 保罗
答案 0 :(得分:2)
首先,如果可以的话,我想稍微改写一下这个方法。
public function addUser(User $user)
{
if ($this->doesUserExist($user)) {
throw new UserAlreadyExistException("The user ".$user->getUid()." already exists.");
}
// ... shortened for brevity
$pbnlAccount = $this->userToEntities($user);
$this->ldapEntityManager->persist($pbnlAccount);
}
其他相关方法是:
private function doesUserExist(User $user)
{
$users = $this->ldapRepository->findByUid($user->getUid());
return count($users) === 1;
}
我们立即可以看到我们基本上有两个测试:
如果你不明白为什么我们有这两个测试,请注意这个方法中有两个可能的“流”:一个执行if语句中的块,另一个不执行它。
让我们解决第一个问题:
public function testAddUserThrowsWhenUserExistsAlready()
{
$user = new User();
$user->setUid('123');
$ldapRepositoryMock = $this->createMock(LdapRepository::class);
$ldapRepositoryMock
->method('findByUid')
->expects($this->once())
->with('123')
->willReturn(new PbnlAccount());
$userRepository = new UserRepository($ldapRepositoryMock);
$this->expectException(UserAlreadyExistException::class);
$userRepository->addUser($user);
}
第二次测试留给读者练习:)
是你将在你的情况下做一些嘲弄。在这种情况下,您将需要模拟LdapRepository和LdapEntityManager。
注1:这段代码可能不可运行,因为我不知道你的代码库的确切细节(我把它写在了我的脑海中),但这不是重点。关键是你要测试异常。
注2:
我会将你的函数重命名为createNewPbnlAccountForUser(User $user)
,这个函数更长,但更具描述性。它实际上做了什么。
注3:
我不确定你为什么要返回$this->getUserByUid()
,因为那似乎是多余的(你已经拥有了用户),所以我省略了这种情况。
答案 1 :(得分:1)
你需要模拟ldapEntityManager和所有存储库服务而不是内部函数。如你所说,不要在单元测试中启动内核。因此,您应该成功测试所有案例并抛出异常(确保检查所有行为)
答案 2 :(得分:0)
如果要执行单元测试,则应模拟所有协作者。 现在,不应该模拟实体管理器,ldap服务等(read more here)。
此外,如果您恰好处于编配部分(设置模拟,存根等)很痛苦并且需要很多事情的情况下。测试,也许这是一种气味,你的班级有太多的责任(做太多事情)。
那就是说,当我进行单元测试时,我希望测试只能因为内部(对班级)的原因而失败,而不是因为我改变了一个混乱的合作者行我所有的测试。