Is it possible to use setup_method with fixtures? - python-2.7

I have the following code:
import pytest
#pytest.fixture
def example_smtp():
return "example"
class TestClass(object):
def test_function(self, example_smtp):
# 1
obj = NewObject(example_smtp)
obj.initialize()
print example_smtp
# here may rise exception
some_action()
# 2
# but we have to cleanup
obj.cleanup()
some_action() may raise a exception, so I want to move 1 and 2 to setup_method and teardown_method, but I don't know how to do it.
setup_method allows only two arguments, so I can't use example_smtp in it.

A better approach is to just write a fixture that creates NewObject for you and cleans up afterwards:
import pytest
#pytest.fixture
def example_smtp():
return "example"
class TestClass(object):
#pytest.yield_fixture(autouse=True)
def obj(self):
obj = NewObject(example_smtp)
obj.initialize()
yield obj
obj.cleanup()
def test_function(self, obj, example_smtp):
# use obj here
some_action(obj)
But if you really prefer to have a "setup_method"-like function (perhaps you are initializing several objects which don't appear in your snippet), you can declare an autouse fixture instead:
import pytest
#pytest.fixture
def example_smtp():
return "example"
class TestClass(object):
#pytest.yield_fixture(autouse=True)
def some_setup(self):
self.obj = ...
# ... setup other objects, perhaps
yield
# ... cleanup everything
self.obj.cleanup()
def test_function(self, example_smtp):
some_action(self.obj)
IMO, there's no compelling reason not to use fixtures when using pytest style test classes (IOW, not subclassing unittest.TestCase) because if you want a single method that does all the setup/cleanup for you you can use an autouse fixture.

I have solved that problem with addfinalizer() function from request object.
import pytest
#pytest.fixture
def example_smtp():
return "example"
class TestClass(object):
#pytest.fixture
def obj(self, request, example_smtp):
print 'initialize', example_smtp
def fin():
print 'finalize'
request.addfinalizer(fin)
def test(self, obj):
some_action_raise_error()
Thank you jonrsharpe for information about yield fixtures.

Related

Understanding Fixtures in Pytest

I have the below function for which I would like to perform a test:
class HighSchool:
...
def tenth_standard(self):
return f"I-am-studying-in-{self.school}-at-{self.country}"
I have a #pytest.fixture that I am using to perform the test, which is as below:
#pytest.fixture(params=["abcd", "efgh"])
def school(request):
return request.param
#pytest.fixture(params=["India", "Japan"])
def country(request):
return request.param
#pytest.fixture
def expected(school, country):
return f"I-am-studying-in-{school}-at-{country}"
def test_tenthstandard(school, country, expected):
b = HighSchool(school=school, country=country)
assert expected == b.tenth_standard()
My doubt here is:
The test case test_tenthstandard takes the fixtures, school and country as input parameters. But the fixture functions school and country are basically doing the same thing. Can we generalize the fixture function and call it for both school and country?
There is no real need for this. You need two different fixtures for the code to work as it does, and as each fixture is a trivial one-liner, I wouldn't go to generalize this. You could do of course:
#pytest.fixture
def base_fixture(request):
return request.param
#pytest.fixture
def school(base_fixture):
return base_fixture
#pytest.fixture
def country(base_fixture):
return base_fixture
That would work, but is hardly an improvement. Keeping the current shorter version is probably the best for this trivial case.

Adding a row in a unittest data base for a FLASK sqlalchemy application

I am trying to write some unittests for my flask application and for some reason my database is always empty.
import os
import unittest
import json
from flask_sqlalchemy import SQLAlchemy
from flaskr import create_app
from models import setup_db, Question, Category, db
class TriviaTestCase(unittest.TestCase):
"""This class represents the trivia test case"""
def setUp(self):
"""Define test variables and initialize app."""
self.app = create_app()
self.client = self.app.test_client
self.database_name = "trivia_test"
self.database_path = "postgres://{}/{}".format('localhost:5432', self.database_name)
setup_db(self.app, self.database_path)
self.question = {
'question': 'is Yaser amazing?',
'answer': 'of course, are you crazy?',
'difficulty': 1 ,
'category': 'all'
}
# binds the app to the current context
with self.app.app_context():
self.db = SQLAlchemy()
self.db.init_app(self.app)
# create all tables
self.db.create_all()
def tearDown(self):
"""Executed after reach test"""
pass
def test_get_questions(self):
res = self.client().get('/questions')
data = json.loads(res.data)
print(data)
self.assertEqual(res.status_code, 200)
self.assertEqual(data['success'],True)
self.assertTrue(data['totalQuestions'])
self.assertTrue(data['all_categories'])
I thought that self.question{} would add a row in my database but it doesn't. I am not sure if the syntax is right or if it should be something else. I am following a class example and I am stumped.
It looks like you're adding data to your test class object but not adding to your database. To demonstrate:
# The following adds a dictionary to `self.question`
self.question = {
'question': 'is Yaser amazing?',
'answer': 'of course, are you crazy?',
'difficulty': 1 ,
'category': 'all'
}
# The following assertion should be true
assert isinstance(self.question, dict)
To add to your database you would need to do the following:
def add_q(self, commit=True):
"""
Unpack the stored dictionary into your db model,
then add to your db.
If you'd like to query this object from the db,
you'll need to commit the session.
--> Toggle this feature with the param `commit`
"""
self.db.session.add(Question(**self.question))
# You can also commit outside of the function execution
# Helpful for batch processing
if commit:
self.db.session.commit()
After doing the above, you should be able to query your database for the newly added question. If you're running this test often, you'll likely want to remove the newly added q. Here's a helpful function for that:
def remove_q(self, commit=True):
"""
Query your db for the previously added q. Then
remove from db.
To commit this change, set commit=True (default)
"""
my_q = self.db.session.query(Question).filter_by(**self.question).first()
# now delete
self.db.session.delete(my_q)
# You can also commit outside of the function execution
# Helpful for batch processing
if commit:
self.db.session.commit()

abstractproperty + classmethod decorators in python

I want to enforce childrens to use a classmethod in python2.7.
I tried this:
import abc
class Base(object):
__metaclass__ = abc.ABCMeta
#abc.abstractproperty
def value(self):
pass
#abc.abstractproperty
#classmethod
def text(cls):
pass
class Imp(Base):
TEXT = "hi im text"
#classmethod
def haba(cls):
print 'HI'
#property
def value(self):
return 'asdasd'
#classmethod
#property
def text(cls):
return 'ho ho p'
print Imp.text
print Imp.TEXT
But I'm getting this output:
<bound method ABCMeta.? of <class 'main.Imp'>>
hi im text
How I can properly enforce childrens to implement classmethod properties?
You can see that Imp.TEXT is working but there is no way to enforce creating this member from base class this way
After re-reading your question a few times I concluded that you want the cl method to behave as if it is a property for the class.
First, Python's implementation of abstract method/property checking is meant to be performed at instantiation time only, not at class declaration. I hope you are aware of that.
Second, Python's descriptor protocol allows for the creation of the equivalent of "class properties", although there is no higher level support for that on the language itself - you can create a class which __get__ method returns your calculated property when the instance argument is None (usually descriptors will return 'self' so that they can be retrieved from the class).
Finally - it is possible that by declaring a custom metaclass being abstract itself, and then declaring it as your class metaclass, abstractproperties will trigger in runtime - let's try that - :
In [1]: import abc
In [2]: class AbsPropertyMeta(abc.ABC, type):
...: #abc.abstractproperty
...: def cl(cls):
...: return "Ho ho ho"
...:
In [3]: class ConcreteExample(metaclass=AbsPropertyMeta):
...: pass
...:
(Note that I will develop the answer using Python 3, which should be what you should be using in any new project or for learning purposes as well)
So, as for the former example, the property in the metaclass does work as a "class property", but Python does not enforce its redefinition in the class body.
So, if you really need this design, you should create a complete custom metaclass for that, and let go of the abc.ABCMeta mechanisms at all:
from functools import partial
def abstractclassproperty(func):
func._abstract_property = True
return func
class clsproperty(object):
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
return self.func(owner)
class ABCAbstractClsProperty(type):
def __new__(mcls, name, bases, namespace, **kw):
new_cls = super(ABCAbstractClsProperty, mcls).__new__(mcls, name, bases, namespace, **kw)
for attr_name in dir(new_cls): # Dir retrieves attributes from all superclasses
attr = getattr(new_cls, attr_name)
if getattr(attr, "im_func", None): # Python 2 specific normalization.
attr = attr.im_func
if getattr(attr, '_abstract_property', False) and new_cls.__dict__.get(attr_name) is not attr:
raise TypeError("Can't create class {!r}: abstract property {!r} not implemented".format(name, attr_name))
return new_cls
""" # Python 3:
class A(metaclass=ABCAbstractClsProperty):
#abstractclassproperty
def cl(cls):
pass
"""
class A(object):
__metaclass__ = ABCAbstractClsProperty
#abstractclassproperty
def cl(cls):
pass
try:
class B(A):
pass
except TypeError:
print("Check ok")
class C(A):
#clsproperty
def cl(cls):
return "ho ho ho " + cls.__name__
print(C.cl)

cached_property and classmethod doesnt work together, Django

I am trying to use cached_property and classmethod decorators together in a viewset but it doesnt work regardless of their mutual position. Is it any chance to make it work together or cached_property doesnt work with classmethod?
Tnanks.
#cached_property
#classmethod
def get_child_extra_actions(cls):
"""
Returns only extra actions defined in this exact viewset exclude actions defined in superclasses.
"""
all_extra_actions = cls.get_extra_actions()
parent_extra_actions = cls.__base__.get_extra_actions()
child_extra_actions = set(all_extra_actions).difference(parent_extra_actions)
return (act.__name__ for act in child_extra_actions)
For cached property with classmethod usage i wrote that code a few days ago:
from django.utils.decorators import classproperty
class cached_classproperty(classproperty):
def get_result_field_name(self):
return self.fget.__name__ + "_property_result" if self.fget else None
def __get__(self, instance, cls=None):
result_field_name = self.get_result_field_name()
if hasattr(cls, result_field_name):
return getattr(cls, result_field_name)
if not cls or not result_field_name:
return self.fget(cls)
setattr(cls, result_field_name, self.fget(cls))
return getattr(cls, result_field_name)
It will be caching result in class-level.
Usage is similar as classproperty:
#cached_classproperty
def some_func(cls, *args, **kwargs):
...
If you do not have django in dependencies, then you may want prevent classproperty parent usage (sources). In that case you may use that decorator:
class cached_classproperty(classproperty):
def __init__(self, method=None):
self.fget = method
def get_result_field_name(self):
return self.fget.__name__ + "_property_result" if self.fget else None
def __get__(self, instance, cls=None):
result_field_name = self.get_result_field_name()
if hasattr(cls, result_field_name):
return getattr(cls, result_field_name)
if not cls or not result_field_name:
return self.fget(cls)
setattr(cls, result_field_name, self.fget(cls))
return getattr(cls, result_field_name)
def getter(self, method):
self.fget = method
return self

Model there should only be one instance of [duplicate]

I would like to control some configuration settings for my project using a database model. For example:
class JuicerBaseSettings(models.Model):
max_rpm = model.IntegerField(default=10)
min_rpm = model.IntegerField(default=0)
There should only be one instance of this model:
juicer_base = JuicerBaseSettings()
juicer_base.save()
Of course, if someone accidentally creates a new instances, it's not the end of the world. I could just do JuicerBaseSettings.objects.all().first(). However, is there a way to lock it down such that it's impossible to create more than 1 instance?
I found two related questions on SO. This answer suggests using 3rd party apps like django-singletons, which doesn't seem to be actively maintained (last update to the git repo is 5 years ago). Another answer suggests using a combination of either permissions or OneToOneField. Both answers are from 2010-2011.
Given that Django has changed a lot since then, are there any standard ways to solve this problem? Or should I just use .first() and accept that there may be duplicates?
You can override save method to control number of instances:
class JuicerBaseSettings(models.Model):
def save(self, *args, **kwargs):
if not self.pk and JuicerBaseSettings.objects.exists():
# if you'll not check for self.pk
# then error will also raised in update of exists model
raise ValidationError('There is can be only one JuicerBaseSettings instance')
return super(JuicerBaseSettings, self).save(*args, **kwargs)
Either you can override save and create a class function JuicerBaseSettings.object()
class JuicerBaseSettings(models.Model):
#classmethod
def object(cls):
return cls._default_manager.all().first() # Since only one item
def save(self, *args, **kwargs):
self.pk = self.id = 1
return super().save(*args, **kwargs)
============= OR =============
Simply, Use django_solo.
https://github.com/lazybird/django-solo
Snippet Courtsy: django-solo-documentation.
# models.py
from django.db import models
from solo.models import SingletonModel
class SiteConfiguration(SingletonModel):
site_name = models.CharField(max_length=255, default='Site Name')
maintenance_mode = models.BooleanField(default=False)
def __unicode__(self):
return u"Site Configuration"
class Meta:
verbose_name = "Site Configuration"
# admin.py
from django.contrib import admin
from solo.admin import SingletonModelAdmin
from config.models import SiteConfiguration
admin.site.register(SiteConfiguration, SingletonModelAdmin)
# There is only one item in the table, you can get it this way:
from .models import SiteConfiguration
config = SiteConfiguration.objects.get()
# get_solo will create the item if it does not already exist
config = SiteConfiguration.get_solo()
If your model is used in django-admin only, you additionally can set dynamic add permission for your model:
# some imports here
from django.contrib import admin
from myapp import models
#admin.register(models.ExampleModel)
class ExampleModelAdmin(admin.ModelAdmin):
# some code...
def has_add_permission(self, request):
# check if generally has add permission
retVal = super().has_add_permission(request)
# set add permission to False, if object already exists
if retVal and models.ExampleModel.objects.exists():
retVal = False
return retVal
i am not an expert but i guess you can overwrite the model's save() method so that it will check if there has already been a instance , if so the save() method will just return , otherwise it will call the super().save()
You could use a pre_save signal
#receiver(pre_save, sender=JuicerBaseSettings)
def check_no_conflicting_juicer(sender, instance, *args, **kwargs):
# If another JuicerBaseSettings object exists a ValidationError will be raised
if JuicerBaseSettings.objects.exclude(pk=instance.pk).exists():
raise ValidationError('A JuiceBaseSettings object already exists')
I'm a bit late to the party but if you want to ensure that only one instance of an object is created, an alternative solution to modifying a models save() function would be to always specify an ID of 1 when creating an instance - that way, if an instance already exists, an integrity error will be raised.
e.g.
JuicerBaseSettings.objects.create(id=1)
instead of:
JuicerBaseSettings.objects.create()
It's not as clean of a solution as modifying the save function but it still does the trick.
I did something like this in my admin so that I won't ever go to original add_new view at all unless there's no object already present:
def add_view(self, request, form_url='', extra_context=None):
obj = MyModel.objects.all().first()
if obj:
return self.change_view(request, object_id=str(obj.id) if obj else None)
else:
return super(type(self), self).add_view(request, form_url, extra_context)
def changelist_view(self, request, extra_context=None):
return self.add_view(request)
Works only when saving from admin