【问题标题】:Compare entities based on their ID during unit test在单元测试期间根据实体 ID 比较实体
【发布时间】:2017-08-10 09:36:44
【问题描述】:

我正在尝试为我的应用程序创建单元测试,但我想获得一些建议。

我有这些方法:

/**
 * This methods check if the user can apply to the team, it search if he have a username for the game and if he doesn't already applied for another team in the same tournament
 * @param User $user
 * @param Team $team
 * @return bool|string
 */
public function canApply(User $user, Team $team) {
    if ($user->getGamingUsername($team->getTournament()->getGame()) === null) {
        return "Vous devez avoir un nom d'utilisateur pour pouvoir vous inscrire, renseignez le dans \"Mon profil\"";
    } else if (false !== $teamAlreadyIn = $this->isAlreadyApplicant($user, $team)) {
        return "Vous avez déjà postulé pour une équipe pour ce tournoi : 
        <a href=\"".$this->router->generate("mgd_team_show", array("id" => $teamAlreadyIn->getId()))."\">".htmlspecialchars($teamAlreadyIn->getName())."</a>";
    }
    return true;
}

/**
 * This method search if the user is already in a team for the same tournament than the one passed in argument
 * @param User $user
 * @param Team $team
 * @return bool|Team|mixed
 */
public function isAlreadyApplicant($user, Team $team) {
    if (!$user || !$this->authorizationChecker->isGranted("ROLE_USER")) {
        return false;
    }

    foreach ($user->getApplications() as $userTeam) {
        /** @var Team $userTeam */
        if ($userTeam->getTournament()->getId() === $team->getTournament()->getId()) {
            return $userTeam;
        }
    }

    foreach ($user->getTeams() as $userTeam) {
        /** @var Team $userTeam */
        if ($userTeam->getTournament()->getId() === $team->getTournament()->getId()) {
            return $userTeam;
        }
    }

    foreach ($user->getManagedTeam() as $userTeam) {
        /** @var Team $userTeam */
        if ($userTeam->getTournament()->getId() === $team->getTournament()->getId()) {
            return $userTeam;
        }
    }
    return false;
}

如您所见,第一个 (canApply) 调用第二个 (isAlreadyApplicant)。

但是当我尝试测试 canApply 时,我遇到了一些麻烦:这个方法调用 isAlreadyApplicant,在这个方法中,我根据比赛的 id 进行比较。

在我的测试类中,我不能“->setId()”,因为它是一个私有方法。那我该怎么处理呢? 从我的数据库中获取元素会更好吗?

目前,我的一个 testMethod 看起来像这样:

/**
 * The user is connected and try to apply to a team and applied for another team for another game
 */
public function testCanApplySecondTeamAnotherGame() {
    $user = new User();
    $game = new Game();
    $anotherGame = new Game();

    $team = new Team();
    $anotherTeam = new Team();

    $tournament = new TournamentTeam();
    $anotherTournament = new TournamentTeam();

    $team->setTournament($tournament);
    $anotherTeam->setTournament($anotherTournament);

    $tournament->setGame($game);
    $anotherTournament->setGame($anotherGame);

    $game->setName("TestGame");
    $anotherGame->setName("AnotherGame");

    $gamingProfile = new GamingProfile();
    $gamingProfile->setGame($game);
    $gamingProfile->setUsername("TestGameUsername");

    $anotherGamingProfile = new GamingProfile();
    $anotherGamingProfile->setGame($anotherGame);
    $anotherGamingProfile->setUsername("TestAnotherGameUsername");

    $user->setGamingProfiles(new ArrayCollection(array($gamingProfile, $anotherGamingProfile)));

    $user->addApplication($anotherTeam);
    $user->addTeam($anotherTeam);

    $router = $this->createMock(Router::class);
    $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
    $authorizationChecker->method("isGranted")->willReturn(true);

    $applicationChecker = new ApplicationChecker($router, $authorizationChecker);
    //Here, we try to apply to a team
    $this->assertTrue($applicationChecker->canApply($user, $team));

}

如果您有任何问题,请不要犹豫! 祝你有美好的一天!

【问题讨论】:

  • 通常不鼓励这样做,但您可以使用 Phpunit 模拟库设置私有属性。
  • 这不是单元测试,而是集成测试。
  • 对不起,我认为集成测试是单元测试

标签: php symfony phpunit symfony-3.2


【解决方案1】:

这段代码耦合度更高,很难以简单的方式进行测试。

我建议你采取方法isAlreadyApplicant 并进入服务例如。

在依赖注入之后将新服务注入到你的实际服务中(我希望是一个服务)。

Dependency Injection Documentation

所以现在您可以使用方法isAlreadyApplicant 模拟新服务。

这是用于单元测试的。


如果您需要功能测试,您可以创建一个类,设置 id 并使用它而不是创建 new Team(),这样您就可以控制您的类,因为您测试的不是实体类而是函数。


另一种解决方案是模拟库以将私有属性设置到您的测试中,但我不喜欢这种解决方案

【讨论】:

  • 是的,这是在同一个服务中,但它不会改变问题,当我测试 isAlreadyApplicant 时,我会遇到同样的问题。如果我必须为每个实体创建另一个实体测试,则执行它需要很多课程,我不想这样做:/
  • 好的,但是你的代码太耦合了,当你需要测试isAlreadyApplicant时你需要用phpunit模拟你的实体你不需要创建类,我建议你分开它并模拟服务和实体@亚历山大
  • 当你说我的代码太耦合时,它只是用于单元测试还是一般?我会把这两个分成两个服务,谢谢你的建议。我设法模拟了实体并且它可以工作(但我会拆分我的服务)
  • 如果可以进行测试,代码一定不能耦合。您可以耦合代码,但之后很难测试,有时甚至是不可能的。如果可能的话,把你的代码解耦,你的测试将很容易用 mock 创建。 :) 很高兴为您提供帮助,如果这解决了您的问题,请接受答案@Alexandre
【解决方案2】:

您应该模拟这些对象,而不是在测试中使用new User()。然后你可以在其中模拟任何方法,所以你不需要使用$user-&gt;setId(),因为你可以得到$userMock-&gt;method('getId')-&gt;willReturn(10)

因此,与其使用 setter 实例化新对象和设置值,不如模拟这些对象并存根 getter。

参考https://phpunit.de/manual/current/en/test-doubles.html

【讨论】:

  • 是的,我设法通过@Alessandro 的回答理解了这一点。感谢您的帮助
猜你喜欢
  • 1970-01-01
  • 2013-10-31
  • 2020-08-31
  • 1970-01-01
  • 2021-12-23
  • 1970-01-01
  • 2023-04-09
  • 2011-05-28
  • 2017-10-23
相关资源
最近更新 更多