I've met an error "fixture 'invalid_email'" not found, while running this test:
import pytest
import time
from ddt import ddt, data, unpack
from pages.home.signup_page import SignUpPage
from utilities.read_data import get_csv_data
#pytest.mark.usefixtures("base_set_up", "set_up")
#ddt
class TestSignUp():
#pytest.fixture(autouse=True)
def class_set_up(self, base_set_up):
self.test_user = SignUpPage(self.driver)
#pytest.mark.run(order=2)
def test_valid_sign_up_with_confirmation(self):
self.test_user.valid_signup()
self.test_user.use_mailer()
result = self.test_user.verify_successful_sign_up()
#pytest.mark.run(order=1)
#data(*get_csv_data('/Users/blabla/utilities/test_data.csv'))
#unpack
def test_invalid_sign_up_invalid_email(self, invalid_email):
self.test_user.invalid_signup(invalid_email)
time.sleep(3)
result = self.test_user.verify_error_sign_up()
What could happen? I've double checked the file test_data.csv for extra spaces, etc.
invalid_email
qqq
111
Error message:
fixture 'invalid_email' not found
available fixtures: base_set_up, browser...
use 'pytest --fixtures [testpath]' for help on them.
I've solved it.
If you click on the decorator, it says:
def data(*values):
"""
Method decorator to add to your test methods.
Should be added to methods of instances of ``unittest.TestCase``.
"""
return idata(values)
So you have to import unittest first, and then do this:
class TestSignUp(unittest.TestCase)
Related
I am trying to write a unit test that will test azure.storage.blob.BlobServiceClient class and its methods. Below is my code
A fixture in the conftest.py
#pytest.fixture
def mock_BlobServiceClient(mocker):
azure_ContainerClient = mocker.patch("azure.storage.blob.ContainerClient", mocker.MagicMock())
azure_BlobServiceClient= mocker.patch("azure_module.BlobServiceClient", mocker.MagicMock())
azure_BlobServiceClient.from_connection_string.return_value
azure_BlobServiceClient.get_container_client.return_value = azure_ContainerClient
azure_ContainerClient.list_blob_names.return_value = "test"
azure_ContainerClient.get_container_client.list_blobs.return_value = ["test"]
yield azure_BlobServiceClient
Contents of the test file
from azure_module import AzureBlob
def test_AzureBlob(mock_BlobServiceClient):
azure_blob = AzureBlob()
# This assertion passes
mock_BlobServiceClient.from_connection_string.assert_called_once_with("testconnectionstring")
# This assertion fails
mock_BlobServiceClient.get_container_client.assert_called()
Contents of the azure_module.py
from azure.storage.blob import BlobServiceClient
import os
class AzureBlob:
def __init__(self) -> None:
"""Initialize the azure blob"""
self.azure_blob_obj = BlobServiceClient.from_connection_string(os.environ["AZURE_STORAGE_CONNECTION_STRING"])
self.azure_container = self.azure_blob_obj.get_container_client(os.environ["AZURE_CONTAINER_NAME"])
My test fails when I execute it with below error message
> mock_BlobServiceClient.get_container_client.assert_called()
E AssertionError: Expected 'get_container_client' to have been called.
I am not sure why it says that the get_container_client wasn't called when it was called during the AzureBlob's initialization.
Any help is very much appreciated.
Update 1
I believe this is a bug in the unittest's MagicMock itself. Per
Michael Delgado suggested that I dialed the code to a bare minimum to test and identify the issue, and I concluded that the MagicMock was causing the problem. Below are my findings:
conftest.py
#pytest.fixture
def mock_Blob(mocker):
yield mocker.patch("module.BlobServiceClient")
test_azureblob.py
def test_AzureBlob(mock_Blob):
azure_blob = AzureBlob()
print(mock_Blob)
print(mock_Blob.mock_calls)
print(mock_Blob.from_connection_string.mock_calls)
print(mock_Blob.from_connection_string.get_container_client.mock_calls)
assert False # <- Intentional fail
After running the test, I got the following results.
$ pytest -vv
.
.
.
------------------------------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------------------------------
<MagicMock name='BlobServiceClient' id='140704187870944'>
[call.from_connection_string('AZURE_STORAGE_CONNECTION_STRING'),
call.from_connection_string().get_container_client('AZURE_CONTAINER_NAME')]
[call('AZURE_STORAGE_CONNECTION_STRING'),
call().get_container_client('AZURE_CONTAINER_NAME')]
[]
.
.
.
The prints clearly show that the get_container_client was seen being called, but the mocked method did not register it at its level. That led me to conclude that the MagicMock has a bug which I will report to the developers for further investigation.
In the below files I have
InternalDogWebhookResource which calls VisitOrchestrator.fetch_visit. I am attempting to write a test for InternalDogWebhookResource but mock VisitOrchestrator.fetch_visit since it is a network call.
I have tried the mock paths:
api.dog.handlers.internal.VisitOrchestrator.fetch_visit
api.dog.handlers.internal.InternalDogWebhookResource.VisitOrchestrator.fetch_visit
api.dog.handlers.internal.InternalDogWebhookResource.fetch_visit
and many others, but I am always getting AssertionError: assert None
I can confirm that the client.post in the test works because when i remove the mock asserts, i get a response back from the api which means fetch_visit is called.
How can I find the mocker.patch path?
api/dog/handlers/internal.py
from api.dog.helpers.visits import VisitOrchestrator
#api.route("/internal/dog/webhook")
class InternalDogWebhookResource():
def post(self) -> JsonResponse:
if event_type == EventType.CHANGE:
VisitOrchestrator.fetch_visit(event['visitId'])
return JsonResponse(status=204)
api/dog/helpers/visits.py
class VisitOrchestrator:
#classmethod
def fetch_visit(cls, visit_id: str) -> VisitModel:
# do stuff
return visit
tests/v0/dog/handlers/test_webhook.py
import pytest
from pytest_mock import MockerFixture
from api.dog.handlers.internal import InternalDogWebhookResource, EventType
from tests.v0.utils import url_for
def test_webhook_valid(client, config, mocker: MockerFixture):
visit_id = '1231231'
mock_object = mocker.patch(
'api.dog.handlers.internal.VisitOrchestrator.fetch_visit',
return_value=visit_id,
)
res = client.post(
url_for(InternalDogWebhookResource),
json={'blag': 'blargh'}
)
assert mock_object.assert_called_once()
You're doing the right things - your second approach is generally the way to go with mocks (mocking api.dog.handlers.internal.InternalDogWebhookResource.VisitOrchestrator.fetch_visit)
I would try to do the minimal test code function:
def test_webhook_valid(mocker):
mock_fetch_visit = mocker.MagicMock(name='fetch_visit')
mocker.patch('api.dog.handlers.internal.VisitOrchestrator.fetch_visit',
new=mock_fetch_visit)
InternalDogWebhookResource().post()
assert 1 == mock_fetch_visit.call_count
If this works for you - maybe the problem is with the client or other settings in your test method.
I have a problem with my pytest. Every time I run the test I get the following message:
In test_signup_customer_homepage: function uses no argument 'browser_name'
Here is my test file:
from nose.tools import istest
from fbtests.pages import Pages
from utils.misc import selenium_testcase
#istest
#selenium_testcase
def test_signup_customer_homepage(driver):
pages = Pages(driver)
pages.home.visit('platform', 'homepage', '')
pages.home.button_signup.wait_element_to_be_clickable()
pages.home.button_signup.click()
Here is #isttest
def istest(func):
func.__test__ = True
return func
Here is #seleniumtestcase
selenium_testcase = make_selenium_testcase(get_selenium_driver)
I have some pytests that all access a fixture whose scope is module. I want to move the duplicated parts of the tests into a common place and access it from there.
Specifically, in the below sample code, in test/test_blah.py each of the test methods has the variable dsn, which is the device under test's serial number. I couldn't figure out how to extract this common code out. I tried accessing the dut in TestBase, but couldn't make it work.
# my_pytest/__init__.py
import pytest
#pytest.fixture(scope="module")
def device_fixture(request):
config = getattr(request.module, 'config', {})
device = get_device(config.get('dsn'))
assert device is not None
return device
...some other code...
# test/base.py
class TestBase:
def common_method_1(self):
pass
def common_method_2(self):
pass
# test/test_blah.py
from base import TestBase
import my_pytest
from my_pytest import device_fixture as dut #'dut' stands for 'device under test'
class TestBlah(TestBase):
def test_001(self, dut):
dsn = dut.get_serialno()
...
# how to extract the dsn = dut.get_serialno() into
# something common so I can keep these tests more DRY?
def test_002(self, dut):
dsn = dut.get_serialno()
...
def test_003(self, dut):
dsn = dut.get_serialno()
...
If I understand your question correctly: Put your fixtures in conftest.py and they will be available to use as arguments for your test functions. No need to import anything, you just define
#pytest.fixture(scope='module')
def dut():
return 'something'
I don't know why I'm just not getting this, but I want to use mock in Python to test that my functions are calling functions in ftplib.FTP correctly. I've simplified everything down and still am not wrapping my head around how it works. Here is a simple example:
import unittest
import ftplib
from unittest.mock import patch
def download_file(hostname, file_path, file_name):
ftp = ftplib.FTP(hostname)
ftp.login()
ftp.cwd(file_path)
class TestDownloader(unittest.TestCase):
#patch('ftplib.FTP')
def test_download_file(self, mock_ftp):
download_file('ftp.server.local', 'pub/files', 'wanted_file.txt')
mock_ftp.cwd.assert_called_with('pub/files')
When I run this, I get:
AssertionError: Expected call: cwd('pub/files')
Not called
I know it must be using the mock object since that is a fake server name, and when run without patching, it throws a "socket.gaierror" exception.
How do I get the actual object the fuction is running? The long term goal is not having the "download_file" function in the same file, but calling it from a separate module file.
When you do patch(ftplib.FTP) you are patching FTP constructor. dowload_file() use it to build ftp object so your ftp object on which you call login() and cmd() will be mock_ftp.return_value instead of mock_ftp.
Your test code should be follow:
class TestDownloader(unittest.TestCase):
#patch('ftplib.FTP', autospec=True)
def test_download_file(self, mock_ftp_constructor):
mock_ftp = mock_ftp_constructor.return_value
download_file('ftp.server.local', 'pub/files', 'wanted_file.txt')
mock_ftp_constructor.assert_called_with('ftp.server.local')
self.assertTrue(mock_ftp.login.called)
mock_ftp.cwd.assert_called_with('pub/files')
I added all checks and autospec=True just because is a good practice
Like Ibrohim's answer, I prefer pytest with mocker.
I have went a bit further and have actually wrote a library which helps me to mock easily. Here is how to use it for your case.
You start by having your code and a basic pytest function, with the addition of my helper library to generate mocks to modules and the matching asserts generation:
import ftplib
from mock_autogen.pytest_mocker import PytestMocker
def download_file(hostname, file_path, file_name):
ftp = ftplib.FTP(hostname)
ftp.login()
ftp.cwd(file_path)
def test_download_file(mocker):
import sys
print(PytestMocker(mocked=sys.modules[__name__],
name=__name__).mock_modules().prepare_asserts_calls().generate())
download_file('ftp.server.local', 'pub/files', 'wanted_file.txt')
When you run the test for the first time, it would fail due to unknown DNS, but the print statement which wraps my library would give us this valuable input:
...
mock_ftplib = mocker.MagicMock(name='ftplib')
mocker.patch('test_29817963.ftplib', new=mock_ftplib)
...
import mock_autogen
...
print(mock_autogen.generator.generate_asserts(mock_ftplib, name='mock_ftplib'))
I'm placing this in the test and would run it again:
def test_download_file(mocker):
mock_ftplib = mocker.MagicMock(name='ftplib')
mocker.patch('test_29817963.ftplib', new=mock_ftplib)
download_file('ftp.server.local', 'pub/files', 'wanted_file.txt')
import mock_autogen
print(mock_autogen.generator.generate_asserts(mock_ftplib, name='mock_ftplib'))
This time the test succeeds and I only need to collect the result of the second print to get the proper asserts:
def test_download_file(mocker):
mock_ftplib = mocker.MagicMock(name='ftplib')
mocker.patch(__name__ + '.ftplib', new=mock_ftplib)
download_file('ftp.server.local', 'pub/files', 'wanted_file.txt')
mock_ftplib.FTP.assert_called_once_with('ftp.server.local')
mock_ftplib.FTP.return_value.login.assert_called_once_with()
mock_ftplib.FTP.return_value.cwd.assert_called_once_with('pub/files')
If you would like to keep using unittest while using my library, I'm accepting pull requests.
I suggest using pytest and pytest-mock.
from pytest_mock import mocker
def test_download_file(mocker):
ftp_constructor_mock = mocker.patch('ftplib.FTP')
ftp_mock = ftp_constructor_mock.return_value
download_file('ftp.server.local', 'pub/files', 'wanted_file.txt')
ftp_constructor_mock.assert_called_with('ftp.server.local')
assert ftp_mock.login.called
ftp_mock.cwd.assert_called_with('pub/files')