Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating fixture data with Python's fixture module

I'm working with the fixture module for the first time, trying to get a better set of fixture data so I can make our functional tests more complete.

I'm finding the fixture module a bit clunky, and I'm hoping there's a better way to do what I'm doing. This is a Flask/SQLAlchemy app in Python 2.7, and we're using nose as a test runner.

So I have a set of employees. Employees have roles. There are a few pages with rather complex permissions, and I'd like to make sure those are tested.

I created a DataSet that has each type of role (there are about 15 roles in our app):

class EmployeeData(DataSet):

  class Meta:
    storable = Employee

  class engineer:
    username = "engineer"
    role = ROLE_ENGINEER

  class manager:
    username = "manager"
    role = ROLE_MANAGER

  class admin:
    username = "admin"
    role = ROLE_ADMIN

and what I'd like to do is write a functional test that checks only the right people can access a page. (The actual permissions are way more complicated, I just wanted a toy example to show you.)

Something like this:

def test_only_admin_can_see_this_page():

  for employee in Employee.query.all():
    login(employee)

    with self.app.test_request_context('/'):
    response = self.test_client.get(ADMIN_PAGE)
    if employee.role == ROLE_ADMIN
      eq_(200, response.status_code)
    else:
      eq_(401, response.status_code)

    logout(employee)

Is there a way to generate the fixture data so my devs don't have to remember to add a line to the fixtures every time we add a role? We have the canonical list of all roles as configuration elsewhere in the app, so I have that.

I'm not wedded to any of this or the fixture module, so I'm happy to hear suggestions!

like image 345
Rachel Sanders Avatar asked Nov 20 '12 19:11

Rachel Sanders


People also ask

What is fixture in Python?

Fixtures define the steps and data that constitute the arrange phase of a test (see Anatomy of a test). In pytest, they are functions you define that serve this purpose. They can also be used to define a test's act phase; this is a powerful technique for designing more complex tests.

What is pytest fixture ()?

Pytest fixtures are functions that can be used to manage our apps states and dependencies. Most importantly, they can provide data for testing and a wide range of value types when explicitly called by our testing software. You can use the mock data that fixtures create across multiple tests.

Can pytest fixtures use other fixtures?

A fixture can use multiple other fixtures. Just like a test method can take multiple fixtures as arguments, a fixture can take multiple other fixtures as arguments and use them to create the fixture value that it returns.

How many times will a fixture of module scope run?

Fixtures with scope session will only be executed once per session. Every time you run pytest , it's considered to be one session. Scope session is designed for expensive operations like truncating table and loading a test set to the database.


1 Answers

An option would be to use factory_boy to create your test data.

  • Assuming that you keep and update accordingly a list of roles (that will be used later on) like this one:

    roles = [ROLE_ENGINEER, ROLE_ADMIN, ROLE_MANAGER, ...]
    
  • Let's create a factory for the Employee table:

    import factory
    from somewhere.in.the.app import roles
    
    class EmployeeFactory(factory.alchemy.SQLAlchemyModelFactory):
        class Meta:
            model = Employee
            sqlalchemy_session = session
    
        username = factory.Sequence(lambda n: u'User %d' % n)
        # Other attributes
        ...
        # Now the role choice
        role = factory.fuzzy.FuzzyChoice(roles)
    

    The FuzzyChoice method takes a list of choices and makes a random choice from this list.
    Now this will be able to create any amount of Employee objects on demand.

  • Using the factory:

    from factory.location import EmployeeFactory
    
    def test_only_admin_can_see_this_page():
        EmployeeFactory.create_batch(size=100)
    
        for employee in session.query(Employee).all():
            login(employee)
    
            with self.app.test_request_context('/'):
            response = self.test_client.get(ADMIN_PAGE)
            if employee.role == ROLE_ADMIN
                eq_(200, response.status_code)
            else:
                eq_(401, response.status_code)
    
            logout(employee)
    

    Breakdown:

    • EmployeeFactory.create_batch(size=100) Creates 100 Employee objects in the test session.
    • We can access those objects from the factory session.

More information about using factory_boy with SQLAlchemy: https://factoryboy.readthedocs.io/en/latest/orms.html?highlight=sqlalchemy#sqlalchemy.
Be careful with session management especially: https://factoryboy.readthedocs.io/en/latest/orms.html?highlight=sqlalchemy#managing-sessions

like image 104
John Moutafis Avatar answered Sep 27 '22 21:09

John Moutafis