0

I'm trying to test that a ModelResource of flask potion conflicts on creation if a one with the same id is present:

class Device(db.Model):
    __tablename__ = 'device'
    uuid = db.Column(UUID, primary_key=True, default=lambda: str(uuid4()))
    make = db.Column(db.String(150), nullable=False)
    model = db.Column(db.String(150), nullable=False)
    category = db.Column(db.String(150), nullable=False)

class DeviceResource(ModelResource):
    class Meta:
        model = Device
        id_converter = 'string'

    class Schema:
        uuid = fields.UUID(io='wr')

and the testing is done as a pytest yield fixture:

@pytest.fixture(scope='session')
def application():
    flask_app = create_app()
    # return a webtest.TestApp
    test_app = TestApp(flask_app)
    test_app.current_app_context = flask_app.app_context
    return test_app

@pytest.yield_fixture(scope='function')
def single_device(application):
    obj = Device(**DEVICE_JSON)
    with application.current_app_context():
        db.session.add(obj)
        db.session.commit()
        yield obj
        for device in Device.query.all():
            db.session.delete(device)
        db.session.commit()

def test_create_device_fail(single_device, application):
     response = application.post_json('/api/v1/device', params=DEVICE_JSON, expect_errors=True)
     assert response.status_code == 409

but when I run I get the following error:

sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: New instance <Device at 0x7fb5e83d5d68> with identity key (<class 'complex_service.db.Device'>, ('1ec67df1-e673-4505-8e00-5be58ec1656a',)) conflicts with persistent instance <Device at 0x7fb5e84419e8>

together with

sqlalchemy.orm.exc.FlushError: New instance <Device at 0x7fb5e83d5d68> with identity key (<class 'complex_service.db.Device'>, ('1ec67df1-e673-4505-8e00-5be58ec1656a',)) conflicts with persistent instance <Device at 0x7fb5e84419e8>

even if I do not pass the fixture inside the test and instead I do a double request, it doesn't work, failing with the same error

def test_create_device_fail(single_device, application):
     application.post_json('/api/v1/device', params=DEVICE_JSON, expect_errors=True)
     assert response.status_code == 200
     response = application.post_json('/api/v1/device', params=DEVICE_JSON, expect_errors=True)
     assert response.status_code == 409

Any idea on how to fix it?

shipperizer
  • 1,641
  • 1
  • 13
  • 19
  • Not sure how exactly pytest works, but it seems that in `single_device()` you add the device, then at `yield` you enter the test? You should probably leave the app context before you yield, and then enter it again to delete the devices after the test. – lyschoening Apr 26 '16 at 09:13
  • making the fixture like this: `@pytest.yield_fixture(scope='function') def single_device(application): obj = Device(**DEVICE_JSON) with application.current_app_context(): db.session.add(obj) db.session.commit() yield obj with application.current_app_context(): for device in Device.query.all(): db.session.delete(device) db.session.commit()` I get a new error: `sqlalchemy.orm.exc.DetachedInstanceError: Instance is not bound to a Session; attribute refresh operation cannot proceed` – shipperizer Apr 26 '16 at 09:25

1 Answers1

0

Thanks to @lyschoening advice I managed to get it working,

@pytest.yield_fixture(scope='function')
def single_device(application):
    obj = Device(**DEVICE_JSON)
    with application.current_app_context():
        db.session.add(obj)
        db.session.commit()
    yield None
    with application.current_app_context():  
        for device in Device.query.all():
            db.session.delete(device)
        db.session.commit()

So basically not yielding the object to the test and having 2 different application contexts

shipperizer
  • 1,641
  • 1
  • 13
  • 19