When it comes to unit testing, you always want to test a unit of your piece of code. Your code might depends on third party services such as an API and you don't want to call it during your local tests or in your CI environment. That's when you should use mocking to mock the external parts or the part you don't want to test.
Masonite comes with some mocking abilities for some of the features relying on third party services.
For other parts or you own code you can use Python mocking abilities provided by unittest.mock
standard module.
Masonite tests case have two helpers method fake()
and restore()
.
You can mock a Masonite feature by doing self.fake(feature)
and then restore it to the real feature behaviour
by calling self.restore(feature)
. When a feature is mocked the real behaviour won't be called, instead
a quick and simple implementation is ran, often offering the ability to inspect and test what happens.
Available features that can be mocked (for now) are:
When mocking emails it will prevent emails from being really sent. Typically, sending mail is unrelated to the code you are actually testing. Most likely, it is sufficient to simply assert that Masonite was instructed to send a given mailable.
Here is an example of how to mock emails sending in your tests:
def setUp(self):
super().setUp()
self.fake("mail")
def tearDown(self):
super().tearDown()
self.restore("mail")
def test_mock_mail(self):
welcome_email = self.application.make("mail").mailable(Welcome()).send()
(
welcome_email.seeEmailContains("Hello from Masonite!")
.seeEmailFrom("[email protected]")
.seeEmailCountEquals(1)
)
Available assertions are:
- seeEmailWasSent()
- seeEmailWasNotSent()
- seeEmailCountEquals(count)
- seeEmailTo(string)
- seeEmailFrom(string)
- seeEmailReplyTo(string)
- seeEmailBcc(string)
- seeEmailCc(string)
- seeEmailSubjectEquals(string)
- seeEmailSubjectContains(string)
- seeEmailSubjectDoesNotContain(string)
- seeEmailContains(string)
- seeEmailDoesNotContain(string)
- seeEmailPriority(string)
When mocking notifications it will prevent notifications from being really sent. Typically, sending notification is unrelated to the code you are actually testing. Most likely, it is sufficient to simply assert that Masonite was instructed to send a given notification.
Here is an example of how to mock notifications sending in your tests:
def setUp(self):
super().setUp()
self.fake("notification")
def tearDown(self):
super().tearDown()
self.restore("notification")
def test_mock_notification(self):
notification = self.application.make("notification")
notification.assertNothingSent()
notification.route("mail", "[email protected]").send(WelcomeNotification())
notification.route("mail", "[email protected]").send(WelcomeNotification())
notification.assertCount(2)
def test_mock_notification(self):
self.application.make("notification").route("mail", "[email protected]").route(
"slack", "#general"
).send(OrderNotification(10))
self.application.make("notification").assertLast(
lambda user, notif: (
notif.assertSentVia("mail")
.assertEqual(notif.order_id, 10)
.assertEqual(
notif.to_slack(user).get_options().get("text"),
"Order 10 has been shipped !",
)
)
)
Available assertions are:
- assertNothingSent()
- assertCount(count)
- assertSentTo(notifiable, notification_class, callable_assert=None, count=None)
- assertLast(callable_assert)
- assertNotSentTo(notifiable, notification_class)
On Notifications instances:
- assertSentVia(*drivers)
- assertEqual(value, reference)
- assertNotEqual(value, reference)
- assertIn(value, container)
Available helpers are:
- resetCount()
- last()
For mocking any piece of code in Python you can use the standard unittest.mock
module. You can find
more information in unittest documentation.
Here is basic example
from unittest.mock import patch
with patch("some.module") as SomeClass:
SomeClass.return_value.my_method.return_value = 0
self.assertEqual(SomeClass().my_method(), 0)
For mocking external HTTP requests you can use the responses
module. You can find more information
in responses documentation.
import responses
@responses.activate
def test_mock_third_party_api(self):
responses.add(responses.POST, "api.github.com", body=b"Ok")
# do something in your code
self.assertTrue(responses.assert_call_count("api.github.com", 1))