Testing Controller Logic With Masonite
Tags: python, framework, masonite, testing
Published: 2018-07-08 02:54:39

---
title: Testing Controller Logic With Masonite
published: true
description: Masonite framework testing controller logic
tags: python, framework, masonite, testing
---

# Introduction

One of the reasons controllers are structured like they are is because of how testable they become. In fact, Masonite itself is extremely testable. The main package has a plethora of features, since it houses the entire framework, and is still ~91% code coverage.

Let’s give an example of a typical controller and show how we can modify it to be more testable. I feel like the best way to go through this article is to give a real world example and then how we can modify it to pass tests.

Masonite is very testable but it does allow some ways to do things that are not testable. For example, Masonite comes with builtin helpers which makes various parts of your code not easily tested without mocking a builtin (which is sort of weird).

I’ll be creating some of these articles on how to refactor various controller methods so be sure to give a follow.

# Our Example
We’ll keep it very simple here so we can go through all the basics without losing anyone. Here is our example Masonite controller:

```python

class PlanController:
    """ Manage User Subscriptions """

    def subscribe(self):
        plan = request().input('plan_id')
        request().user().subscribe(plan, request().input('stripeToken'))

        return request().redirect('/plans')
```

# Problem 1
If we go to test this, it might look something like this:

```python
from app.http.controllers.PlanController import PlanController

class TestPlanController:

    def setup_method(self):
        self.controller = PlanController()
    
    def test_controller_subscribes_user(self):
        assert self.controller.show()
```

This will start throwing a whole bunch of errors because:

1. Python testing doesn’t know what the hell a `request()` is because it’s a builtin function that is activated by a Service Provider when the application first boots up.
2. It’s not correctly able to get the input because we never specified an input.

# Refactoring
Let’s refactor the controller so we can at least pass in a request:

```python
from masonite.request import Request

class PlanController:
    """ Manage User Subscriptions """

    def subscribe(self, request: Request):
        plan = request.input('plan_id')
        request.user().subscribe(plan, request.input('stripeToken'))

        return request.redirect('/plans')
```

Ok awesome. Now we can at least pass in the request class so let’s go back to our controller test:

```python
from app.http.controllers.PlanController import PlanController
from masonite.request import Request
from masonite.testsuite.TestSuite import generate_wsgi

class TestPlanController:

    def setup_method(self):
        self.request = Request(generate_wsgi())
        self.controller = PlanController()
    
    def test_controller_subscribes_user(self):
        assert self.controller.show(self.request)
```

Perfect! Now we can at least mock the request class. We didn’t really need to mock the request class because we can just use the real class and mock the WSGI request. 

Notice though that we imported a `generate_wsgi` function into the code from the Masonite test suite. Masonite has a few helper classes and functions used for testing which we can go through in another article.

# Problem 2
Ok so this should STILL not work because we didn’t actually load in a user into the `request.user()` method. It should always return `None`. 

Let’s have a request class where we have a user already loaded in. This user will require a `subscribe` method and we will also check if the user is subscribed so let’s just mock that up.

We will also mock up a new request class so we can add any methods we need to it in the future:

```python
from app.http.controllers.PlanController import PlanController
from masonite.request import Request
from masonite.testsuite.TestSuite import generate_wsgi

class MockUser:
    def subscribe(self, plan, token):
        self.plan = plan

    def is_subscribed(self, plan):
        if self.plan == plan:
            return True

        return False

class MockRequest(Request):

    def user(self):
        return MockUser

class TestPlanController:

    def setup_method(self):
        self.request = MockRequest(generate_wsgi())
        self.controller = PlanController()
    
    def test_controller_subscribes_user(self):
        assert self.controller.show(self.request)
```

Awesome. Now we can run the `request.user()` method and it will return this `MockUser` class.

# Problem 3
We do not have the correct inputs now. We can also easily mock those up:

```python
from app.http.controllers.PlanController import PlanController
from masonite.request import Request
from masonite.testsuite.TestSuite import generate_wsgi

...

class TestPlanController:

    def setup_method(self):
        self.request = MockRequest(generate_wsgi())
        self.controller = PlanController()
    
    def test_controller_subscribes_user(self):
        # Sets input data
        self.request.request_variables = {'plan_id': 'premium', 'stripeToken': 'tok_amex'}

        assert self.controller.show(self.request)
```

GREAT! Now we have everything we need for the test to pass correctly. Now let’s just do 1 more assertion:

```python
    ...
    ...
    def test_controller_subscribes_user(self):
        # Sets input data
        self.request.request_variables = {'plan_id': 'premium', 'stripeToken': 'tok_amex'}

        assert self.controller.show(self.request)
        assert self.request.user().is_subscribed()
```

We have successfully verified that this controller method successfully subscribes users!