Viewed   295 times

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('GlennRepositoriesUserUserRepositoryInterface');

        $authorizedUser = Mockery::mock('IlluminateAuthAuthManager');
        $authorizedUser->shouldReceive('user')->once()->andReturn(true);

        $redirect = Mockery::mock('IlluminateRoutingRedirector');
        $redirect->shouldReceive('to')->once()->andReturn('redirected to home');

        $view = Mockery::mock('IlluminateViewEnvironment');


        # 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?

 Answers

5

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('GlennRepositoriesUserUserRepositoryInterface');
}

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('IlluminateAuthAuthManager');
    $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('IlluminateAuthManager', $this->getAuthMock(true));

        // Act
        $this->call('/your/route/to/controller/method', 'GET');

        // Assert
        $this->assertRedirectedTo('/');

    }

    protected function getAuthMock($isLoggedIn)
    {
        $authMock = Mockery::mock('IlluminateAuthManager');
        $authMock->shouldReceive('user')->once()->andReturn($isLoggedIn);
        return $authMock;
    }
}
Sunday, November 20, 2022
 
cdrev
 
3

Service object can have an interface like this

type Service struct {
    serviceK typeK
    serviceHttp serviceHttp // of type interface hence can be mocked
}

Normal application code can init services with actual objects. Tests will have mocks objects

type Req struct {
}

type Resp struct {
}

type ServiceHttp interface{
    HttpReq(params Req)(Resp, error)
}

type Implementation struct {
}

func (i *Implementation)HttpReq(params Req)(Resp, error){
   // buid http request
}

func (c *Service) invoke(Connection Connection, args []string) {

    service, err := c.getService(Connection, args)

    serviceC, err := c.getServiceK(service, []string{"url", “user”, “id”})
    c := strings.Fields(serviceC)

    serviceImp := c.GetServiceImp()

    // init params with the required fields
    val, err := c.HttpReq(params)
    if err != nil {
      fmt.println(err)
    }
    fmt.Print(string(val))

}

when you are running tests, you can initialise the service object with mock Implementation that returns a dummy response.

type MockImplementation struct {
}

func (i *MockImplementation)HttpReq(Resp, error){
    // return mock response
}

func TestMain(){
  services := {
     serviceHttp:MockImplementation{},
     serviceK: typeK{}, // initialise this
  }
}

This is one of way testing it. Other way could be I guess where httpReq return http.ResponseWriter and you can use httptest.ResponseRecorder to test it.

Tuesday, December 6, 2022
4

I have done this before as follows - my console command returns a json response:

public function getConsoleResponse()
{
    $kernel = $this->app->make(IlluminateContractsConsoleKernel::class);
    $status = $kernel->handle(
        $input = new SymfonyComponentConsoleInputArrayInput([
            'command' => 'test:command', // put your command name here
        ]),
        $output = new SymfonyComponentConsoleOutputBufferedOutput
    );

    return json_decode($output->fetch(), true);
}

So if you want to put this in it's own command tester class, or as a function within TestCase etc... up to you.

Wednesday, August 24, 2022
 
mattyb
 
4

I solved the problem mocking the the SecurityComponent::_validatePost method:

$this->Users = $this->generate('Users', array(
    'components' => array(
        'Security' => array('_validatePost'),
    )
));

Inspired by Dealing with Security component in a CakePHP 2 test case

Tuesday, October 18, 2022
 
4

If your various user classes inherit from a parent user class, then I recommend you use the same inheritance structure for your test case classes.

Consider the following sample classes:

class User
{
    public function commonFunctionality()
    {
        return 'Something';
    }

    public function modifiedFunctionality()
    {
        return 'One Thing';
    }
}

class SpecialUser extends User
{
    public function specialFunctionality()
    {
        return 'Nothing';
    }

    public function modifiedFunctionality()
    {
        return 'Another Thing';
    }
}

You could do the following with your test case classes:

class Test_User extends PHPUnit_Framework_TestCase
{
    public function create()
    {
        return new User();
    }

    public function testCommonFunctionality()
    {
        $user = $this->create();
        $this->assertEquals('Something', $user->commonFunctionality);
    }

    public function testModifiedFunctionality()
    {
        $user = $this->create();
        $this->assertEquals('One Thing', $user->commonFunctionality);
    }
}

class Test_SpecialUser extends Test_User
{
    public function create() {
        return new SpecialUser();
    }

    public function testSpecialFunctionality()
    {
        $user = $this->create();
        $this->assertEquals('Nothing', $user->commonFunctionality);
    }

    public function testModifiedFunctionality()
    {
        $user = $this->create();
        $this->assertEquals('Another Thing', $user->commonFunctionality);
    }
}

Because each test depends on a create method which you can override, and because the test methods are inherited from the parent test class, all tests for the parent class will be run against the child class, unless you override them to change the expected behavior.

This has worked great in my limited experience.

Wednesday, December 14, 2022
 
michal
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :