Search code examples
phpsymfonyphpunitsymfony-3.2

Compare entities based on their ID during unit test


I'm trying to create unit test for my application but I would like to have some advice.

I have these methods :

/**
 * 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;
}

As you can see, the first (canApply) call the second one (isAlreadyApplicant).

But when I try to test canApply, I have some trouble : this method call isAlreadyApplicant and in this one I compare based on the id the tournaments.

In my test class, I can't "->setId()" because it's a private method. So how am I supposed to handle this? Is it better to get an element from my database?

At the moment, one of my testMethod looks like this :

/**
 * 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));

}

If you have any question, don't hesitate! Have a good day!


Solution

  • This code is more coupled and will be difficult to test in an easy way.

    I advise you to take method isAlreadyApplicant and move into a service for example.

    After with the dependency injection inject the new service into your actual service (I hope is a service).

    Dependency Injection Documentation

    So now you can mock the new service with method isAlreadyApplicant.

    This is for unit test.


    If you need functional tests you can create a class, set the id and use it instead of create new Team() so you can control your class because you aren't testing the entity class but the function.


    Another solution is to mock library to set private properties into your tests but I don't like this solution