6

I have begun writing unit tests for my Flask API. I have gotten them to work when declared outside of a class. However, for simplicity and OOP constraints, I am trying to have everything run from a class. The issue is I cannot seem to pass any fixture methods to my test class. The code I have here is as follow:

#conftest.py

import os, json, pytest
from ..app import create_app
from flask import Flask

@pytest.fixture
def env_setup():
    env_name = os.getenv('FLASK_ENV')
    app = create_app(env_name)
    return app

I am trying to import env_setup into the following file.

# test_BaseURL.py
import pytest

@pytest.mark.usefixtures("env_setup")
class TestStaticPages:

    def setUp(self, env_setup):
        """
        Setup Test
        """
        self.client = env_setup.test_client()

    def test_base_route(self, env_setup):
        #client = env_setup.test_client()
        url   = '/'
        html1 = b'Welcome to the API. Please visit '
        html2 = b'https://example.com to learn more about this app.'

        response = self.client.get(url)
        assert response.get_data() == html1 + html2
        assert response.status_code == 200

I keep geeting the following error when I run this test:

>       response = self.client.get(url)
E       AttributeError: 'TestStaticPages' object has no attribute 'client'

src/tests/test_BaseURL.py:18: AttributeError

However if I should uncomment the line with client = env_setup.test_client() it works. For whatever reason it cannot seem to grab the setup from the setUP method and keeps erroring out.

cp-stack
  • 785
  • 3
  • 17
  • 40
  • First of all, `setUp` is a `unittest.TestCase` method, but your class doesn't inherit from it, so it will not be called. Second, you can't use neither `unittest`- nor `xUnit`-style setup methods to inject fixture values - use an autouse fixture instead. Check out [this answer](https://stackoverflow.com/a/50135020/2650249) for an example. – hoefling Jan 23 '20 at 21:08
  • @hoefling thanks for the answer. pytest does support unittest cases however. I am just not sure I am doing it right. – cp-stack Jan 23 '20 at 21:12
  • 1
    Yes; `pytest` supports _running_ plain `unittest` test cases out of the box. However, 1. _The following pytest features do not work, and probably never will due to different design philosophies: Fixtures_ (reference: [pytest features in `unittest.TestCase` subclasses](https://docs.pytest.org/en/latest/unittest.html#pytest-features-in-unittest-testcase-subclasses)) and 2. the example in your question is not a `unittest` test case. You are trying to use a setup method from a `unittest` test class in a vanilla `pytest` test class, this simply won't work. – hoefling Jan 23 '20 at 21:23
  • Thanks for the tips. I was able to use your suggestions to solve the problem. – cp-stack Jan 24 '20 at 17:45

1 Answers1

17

Here is how I fixed my issue:

#conftest.py
import os, json, pytest
from ..app import create_app
from flask import Flask

@pytest.fixture
def client():
    env_name = os.getenv('FLASK_ENV')
    app = create_app(env_name)
    client = app.test_client()
    return client

I was then able to import the client into my other test class like so.

#test_StaticView.py
import pytest

@pytest.mark.usefixtures("client")
class TestStaticPages:

    def test_base_route(self, client):
        url   = '/'
        html1 = b'Welcome to the API. Please visit '
        html2 = b'https://example.com to learn more about this app.'

        response = client.get(url)
        assert response.get_data() == html1 + html2
        assert response.status_code == 200
cp-stack
  • 785
  • 3
  • 17
  • 40
  • 4
    One thousand upvotes for this. 2 hours of scouring the internet. Using fixtures within a class in pytest got me here. The pytest class had to have the `usefixtures` decorator! – MrYutz Mar 04 '22 at 15:32
  • 1
    When using `from unittest.mock import patch`, apply the `@patch('dot.path')` decorator to the *method* and provide the parameter to catch the Mock object *first* in the parameter list. Then when using `@pytest.mark.usefixtures("fixture_name")` decorator, apply that at the *class* level and subsequent paramters on the methods will catch those. – NeilG Dec 16 '22 at 14:17
  • 1
    If I may add... this wasn't working for me because I was using `unittest.TestCase` to define my class (as in `class TestMyStuff(TestCase):` and this is NOT SUPPORTED. See [pytest docs right here](https://doc.pytest.org/en/6.2.x/unittest.html#pytest-features-in-unittest-testcase-subclasses). I removed TestCase and it works exactly as in this answer. – daevski Dec 16 '22 at 20:53