Testing
Introduction
Masonite testing is very simple. You can test very complex parts of your code with ease by just extending your class with a Masonite unit test class.
Although Masonite uses pytest
to run tests, Masonite's test suite is based on unittest
. So you will use unittest
syntax but run the tests with Pytest. Because of this, all syntax will be in camelCase
instead of PEP 8 under_score
. Just know that all TestCase
method calls used during testing is in camelCase
form to maintain unittest standards.
Normal tests should still be underscore and start with test_
though like this:
Configuration
First, create a new test class in a testing directory. There is a craft command you can run to create tests for you so just run:
This will create a user test for us which we can work on. You can drag this test in any subdirectory you like.
This command will create a basic test like the one below:
That's it! You're ready to start testing. Read on to learn how to start building your test cases.
Environments
Most times you want to develop and test on different databases. Maybe you develop on a local MySQL database but your tests should run in a SQLlite database.
You can create a .env.testing
file and put all database configs in that. When Pytest runs it will additionally load and override any additional environment variables.
Your .env.testing
file may look like this:
Feel free to load any testing environment variables in here. By default they will not be commited.
Available Assertion Methods
Here is a list of methods that can be used for assetions.
All methods that begin with assert
can be chained together to run through many assertions. All other method will return some kind of boolean or value which you can use to do your own assertions.
assertContains(value) | assertHasAmount(amount) | assertHeaderIs(key, value) | |
assertNotFound() | assertNotHasAmount(amount) | assertPathIs(value) | |
assertHasJson(key, value) | assertParameterIs(parameter, value) | isNamed(name) | |
assertJsonContains(key, value) | assertIsStatus(code) | hasMiddleware(*middleware) | |
assertCount(number) | assertHasHeader(name) | assertNotHasHeader(name) | hasController(name) |
contains(value) | ok() | canView() | |
hasJson(key, value) | count(number) | amount(number) | |
isGet() | isPost() | isPut() | |
isPatch() | isDelete() | hasSession(key, value) | |
parameterIs() | headerIs() |
Calling Routes
We have a few options for testing our routes.
Testing If a Route Exists:
To check if a route exists, we can simple use either get or post:
Getting the Request and Response
The request and responses of a test are gotten by accessing the request
and response
attributes. The response
attribute will be a string representation of your route:
Method options
You can choose anyone of the normal request methods:
JSON Requests
You can use a standard JSON request and specify whichever option you need using the json()
method:
Testing If Route Has The Correct Name
Testing If A Route Has The Correct Middleware
Testing If A Route Contains A String
This can be used to see if the template returned a specific value
You can also use:
Checking 200 Status Code
You can easily check if the response is ok by using the ok
method:
CSRF Protection
By default, all calls to your routes with the above methods will be without CSRF protection. The testing code will allow you to bypass that protection.
This is very useful since you don't need to worry about setting CSRF tokens on every request but you may want to enable this protection. You can do so by calling the withCsrf()
method on your test.
This will enable it on a specific test but you may want to enable it on all your tests. You can do this by adding the method to your setUp()
method:
Exception Handling
As you have noticed, Masonite has exception handling which it uses to display useful information during development.
This is an issue during testing because we wan't to be able to see more useful testing related issues. Because of this, testing will disable Masonite's default exception handling and you will see more useful exceptions during testing. If you want to use Masonite's built in exception handling then you can enable it by running:
Getting Output
You can get the output by using the capture output easily by calling the captureOutput
method on your unit test:
Testing JSON
A lot of times you will want to build tests around your API's. There are quite a few methods for testing your endpoints
Testing the count
You can test to make sure your endpoint returns a specific amount of something. Like returning 5 articles:
You can also use assertCount(5)
:
You can also use amount
which is just an alias for count
:
Checking Amount of Specific Key
You can also check if a specific JSON key has a specific amount. For example:
Testing Specific Responses
Sometimes you will want to check if your endpoint returns some specific JSON values:
You can also specify a dictionary of values as well and will check if each value inside the response:
You do not have to specify all of the elements. Just the ones you want to check for.
You can alo use:
Asserting values inside a collection
You can also assert values inside a list of responses:
Dot Notation
You can also use dot notation for multi dimensional endpoints:
Converting to a Dictionary
Sometimes you don't want to use dot notation and may choose to convert directly to a dictionary and assert values on that. This is also good for debugging so you can print the dictionary to these terminal. You can do this easily:
Testing Parameters
You can test if a specific parameter contains a specific value. For example if you want to see if the parameter id
is equal to 5
:
Testing Headers
You can test if a specific header contains a specific value. For example if you want to see if the header Content-Type
is equal to text/html
:
Testing Status
You can use the isStatus
and assertIsStatus
methods to assert status checks:
You can also easily assert 404
methods:
This is the same as asserting a 404
status code.
Subdomains
By default, Masonite turns off subdomains since this can cause issues when deploying to a PaaS that deploys to a subdomain like sunny-land-176892.herokuapp.com
for example.
To activate subdomains in your tests you will have to use the withSubdomains()
method. You can then set the host in the wsgi
attribute.
Testing the Controllers
Testing view template name
To check that the controller renders the correct view name we can use assertViewIs
Testing view data
To check that the view is rendered with at least one data key we can use assertViewHas
. This assertion methods takes one data key and optionally its associated value.
To check all data a view is rendered with we can use assertViewHasAll
. This assertion methods takes a list of data keys or the whole data dictionary (both keys and values will be verified in this case).
To check that the view is rendered without a given data key we can use assertViewMissing
.
Testing the Database
Databases
By default, to prevent messing with running databases, database test cases are set to only run on the sqlite
database. You can disable this by setting the sqlite
attribute to False
.
This will allow you to use whatever database driver you need.
Transactions and Refreshing
By default, all your tests will run inside a transaction so any data you create will only exist within the lifecycle of the test. Once the test completes, your database is rolled back to its previous state. This is a perfect way to prevent test data from clogging up your database.
Although this is good for most use cases, you may want to actually migrate and refresh the entire migration stack. In this case you can set the refreshes_database
attribute to True
.
Now this will migrate and refresh the database.
Beware that this will destroy any database information you have.
Factories
Factories are simply ways to seed some dummy data into your database. You can create a factory by making a method that accepts a faker argument and using that to seed data.
Masonite has a convenient method you can use that will run once the test first boots up called setUpFactories
. This will run once and only once and not between every test.
Let's create the factory as well as use the setUpFactories method to run them now.
Below is how to create 100 users:
You don't need to build factories though. This can be used to simply create new records:
Users
We can load users into the route and check if they can view the route. This is good to see if your middleware is acting good against various users. This can be done with the acting_as()
method.
Passing in Data
Maybe you need to check a post request and pass in some input data like submitting a form. You can do this by passing in a dictionary as the second value to either the get
or post
method:
The same can be applied to the get method except it will be in the form of query parameters.
Asserting Values
Instead of doing model calls to check if values exist in the database, you can use a simple assertDatabaseHas
assertion:
You can also do the same thing with the opposite assertDatabaseNotHas
assertion:
Test Example
To complete our test, let's check if the user is actually created:
Thats it! This test will now check that the user is created properly
Running Tests
You can run tests by running:
Last updated