After finally getting my stupid simple test to pass, I have a feeling that I'm not doing it correctly.
I have a SessionsController, that is responsible for displaying a login page and logging a user in.
I have decided not to use facades so that I wouldn't have to extend Laravel's TestCase and take a performance hit on my unit tests. Therefore, I have injected all the dependencies through the controller, like so -
SessionsController - Constructor
public function __construct(UserRepositoryInterface $user,
AuthManager $auth,
Redirector $redirect,
Environment $view )
{
$this->user = $user;
$this->auth = $auth;
$this->redirect = $redirect;
$this->view = $view;
}
I have done the necessary declaring of variables and using the namespaces, which I'm not going to include here as its unnecessary.
the create method detects if a user is authorized, if they are then I redirect them to the home page, otherwise they are displayed the login form.
SessionsController - Create
public function create()
{
if ($this->auth->user()) return $this->redirect->to('/');
return $this->view->make('sessions.login');
}
Now for the testing, I'm brand new to it, so bear with me.
SessionsControllerTest
class SessionsControllerTest extends PHPUnit_Framework_TestCase {
public function tearDown()
{
Mockery::close();
}
public function test_logged_in_user_cannot_see_login_page()
{
# Arrange (Create mocked versions of dependencies)
$user = Mockery::mock('Glenn\Repositories\User\UserRepositoryInterface');
$authorizedUser = Mockery::mock('Illuminate\Auth\AuthManager');
$authorizedUser->shouldReceive('user')->once()->andReturn(true);
$redirect = Mockery::mock('Illuminate\Routing\Redirector');
$redirect->shouldReceive('to')->once()->andReturn('redirected to home');
$view = Mockery::mock('Illuminate\View\Environment');
# Act (Attempt to go to login page)
$session = new SessionsController($user, $authorizedUser, $redirect, $view);
$result = $session->create();
# Assert (Return to home page)
}
}
This all passes, but I don't want to have to declare all of these mocked dependencies for each test that I write in my SessionsControllerTest. Is there a way to declare these mocked dependencies once in say a constructor? and then call them by there variables for mocking?
When testing Laravel applications, you may wish to "mock" certain aspects of your application so they are not actually executed during a given test. For example, when testing a controller that dispatches an event, you may wish to mock the event listeners so they are not actually executed during the test.
PHPUnit is a unit testing framework for the PHP programming language. It is an instance of the xUnit design for unit testing systems that began with SUnit and became popular with JUnit. Even a small software development project usually takes hours of hard work.
To Run a single PHPUnit Test Case on a method within the file: Open your PHPUnit Test Case file in the editor. Place your cursor on the method you wish to test, right-click and select Run As | PHP Unit Test.
PHPUnit is a programmer-oriented testing framework for PHP. It is an instance of the xUnit architecture for unit testing frameworks. PHPUnit 9 is the current stable version. PHPUnit 10 is currently in development.
You can use the setUp
method to declare any dependencies that are global for the entire test class. It's similar to the tearDown
method you're currently using:
public function setUp()
{
// This method will automatically be called prior to any of your test cases
parent::setUp();
$this->userMock = Mockery::mock('Glenn\Repositories\User\UserRepositoryInterface');
}
However that won't work if your set up for the mock differs between tests. For this case you can use a helper method:
protected function getAuthMock($isLoggedIn = false)
{
$authorizedUser = Mockery::mock('Illuminate\Auth\AuthManager');
$authorizedUser->shouldReceive('user')->once()->andReturn($isLoggedIn);
}
Then when you need the auth mock you can just call getAuthMock
. This can greatly simplify your tests.
However
I don't think you're testing your controller correctly. You shouldn't instantiate the controller object yourself, instead you should utilize the call
method which exists in Laravel's TestCase
class. Try checking out this article about testing Laravel Controllers by Jeffrey Way. I think you're looking to do something more along the lines of this in your test:
class SessionsControllerTest extends TestCase
{
public function setUp()
{
parent::setUp();
}
public function tearDown()
{
Mockery::close();
}
public function test_logged_in_user_cannot_see_login_page()
{
// This will bind any instances of the Auth manager during
// the next request to the mock object returned from the
// function below
App::instance('Illuminate\Auth\Manager', $this->getAuthMock(true));
// Act
$this->call('/your/route/to/controller/method', 'GET');
// Assert
$this->assertRedirectedTo('/');
}
protected function getAuthMock($isLoggedIn)
{
$authMock = Mockery::mock('Illuminate\Auth\Manager');
$authMock->shouldReceive('user')->once()->andReturn($isLoggedIn);
return $authMock;
}
}
Yes, you can use a "helper". Move the creation of the mocked dependencies into another function, then call that when you need them. Check out slide 52 in this presentation: https://speakerdeck.com/jcarouth/guiding-object-oriented-design-with-tests-1 (well check out the whole thing, but the example is on slide 52)
Edit: The setUp way is even better, I was thinking for something you didn't need in ALL the tests, but I think for what you described doing it in the setUp is way better.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With