Understanding Fixtures in Pytest - unit-testing

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.

Related

Django REST Framework: DRY way to specify viewmodels

I would like to have a clear way for declaring a viewmodel for my Django REST endpoints, which the incoming requests must adhere to and which would take care of the validation. I mean something like viewmodels in Spring or other Java projects.
The most primitive way is just to work with the request.data (or request.POST) dictionary-like object, but in this case all the validation is hardcoded to the view layer.
A little better approach would be to use serializers, but using them is still quite verbose and in the end you get back a dict, unless you implement the .create() method witch makes the code even more verbose. Here is my current approach:
class MyView(APIView):
def post(self, request, format=None):
serializer= MySerializer(data=request.data)
serializer.is_valid(raise_exception=True)
what_i_actually_need = serializer.validated_data
...
return Response('Something')
Is there a more DRY way to do it?
Inspired by the Spring approach I implemented something similar. Let me first show the public API and then include the code behind it.
class GreetingViewModel(ViewModel):
first_name= serializers.CharField(max_length=100)
last_name= serializers.CharField(max_length=120)
class GreetingView(APIView):
def post(self, request, format=None):
# ViewModel will be validated here
viewmodel = GreetingViewModel.get_viewmodel(request.data)
return Response({'message': f'Good afternoon, '
'dear {viewmodel.first_name} {viewmodel .last_name}!'})
And here is the code behind it:
class ViewModel(serializers.Serializer):
"""
Used to strictly specify a viewmodel expected from the request body.
Call `get_viewmodel` to convert request body to a Python object after it is validated.
"""
#classmethod
def get_viewmodel(cls, data):
instance = cls(data=data)
instance.is_valid(raise_exception=True)
return instance.save()
def create(self, validated_data):
obj = type('ViewModel', (), validated_data)()
return obj
Note that this is just my personal idea, so I appreciate comments and suggestions.

Is it possible to use setup_method with fixtures?

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.

Return httpresponse outside of post

Is there a way to stop execution and return an httpresponse in a function other than post, get, put?
So for example
class MyClass(View):
def post(self, request, *args, **kwargs):
test_some_things()
do_some_other_stuff()
return HttpResponse(..)
def test_some_things(self):
if test_fails:
return HttpResponse(..)
else:
return 1
I want to be able to end execution if test_fails and just return the response. But the above doesn't seem to work...
Although possible, it clearer to separate these things: have one method that tests things and returns the outcome of that (boolean). Then, check whether the tests succeeded and return the response in the view.
This will make your code a lot easier to maintain and test in the long run.
What your propose is possible but it won't lead to the simplest solution to this situation.

Restarting FormWizard from done method

I'm trying use the FormWizard for to submit an order "charge" in wizard done method. Extending the example in the documentation, performing the credit card "charge" in the done means you can't go back and reprompt for credit card, because the wizard performs self.storage.reset() after calling the done method.
What is the right approach? The confirmation form clean() step is called multiple times for revalidation, etc, & seems too removed from done() where all validated forms are available.
Thanks for any pointers.
Kent
I could think of this:
In done() method you will be charging the user. If its declined/failed, save each forms data in session/cookies.
Restart the wizard from specific step where payment info is taken. NamedUrlWizard might be helpful.
Implement your get_form_intial() to return data from session/cookies for the step.
However, I think validation of this may fail as skipped steps do not have data. So you may have to do some more to get pass that.
I guess the answer is "you can't get there from here". I opened a ticket #19189, but it's unclear this feature will be added.
Here is my solution:
1. extend WizardView, modify render_done method to catch exception in it:
- detailed description
from django.contrib.formtools.wizard.views import SessionWizardView
class MySessionWizardView(SessionWizardView):
def __init__(self, **kwargs):
super(MySessionWizardView, self).__init__(**kwargs)
self.has_errors = False
class RevalidationError(Exception):
def __init__(self, step, form, **kwargs):
self.step = step
self.form = form
self.kwargs = kwargs
def __repr__(self):
return '%s(%s)' % (self.__class__, self.step)
__str__ = __repr__
def render_done(self, form, **kwargs):
final_form_list = []
for form_key in self.get_form_list():
form_obj = self.get_form(step=form_key,
data=self.storage.get_step_data(form_key),
files=self.storage.get_step_files(form_key))
if not form_obj.is_valid():
return self.render_revalidation_failure(form_key, form_obj, **kwargs)
final_form_list.append(form_obj)
try:
done_response = super(MySessionWizardView, self).render_done(form, **kwargs)
except self.RevalidationError as e:
return self.render_revalidation_failure(e.step, e.form, **e.kwargs)
self.storage.reset()
return done_response

Using class based views to process information?

I've been experimenting with Django's Class Based Views and am trying to write a simple class based view that processes certain information in request so that the processed information can be used by the "handler" method.
I don't seem to have fully understood what the docs say and am unsure of whether this should be a Mixin, a generic view or something else. I'm thinking of making a class like this:
class MyNewGenericView(View):
redirect_on_error = 'home'
error_message = 'There was an error doing XYZ'
def dispatch(self, request, *args, **kwargs):
try:
self.process_information(request)
# self.process_information2(request)
# self.process_information3(request)
# etc...
except ValueError:
messages.error(request, self.error_message)
return redirect(self.redirect_on_error)
return super(MyNewGenericView, self).dispatch(request, *args, **kwargs)
def process_information(self, request):
# Use get/post information and process it using
# different models, APIs, etc.
self.useful_information1 = 'abc'
self.useful_information2 = 'xyz'
def get_extra_info(self):
# Get some extra information on something
return {'foo':'bar'}
This will allow someone to write a view like:
class MyViewDoesRealWork(MyNewGenericView):
def get(self, request, some_info):
return render(request, 'some_template.html',
{'info':self.useful_information1})
def post(self, request, some_info):
# Store some information, maybe using get_extra_info
return render(request, 'some_template.html',
{'info':self.useful_information1})
Is the above code the right way to go? Is there any simpler/better way of doing this? Will this prevent the above functionalities from being used in another generic view (e.g. a built-in generic view)?
Have a look at this. great example code. http://www.stereoplex.com/blog/get-and-post-handling-in-django-views
It seems I just asked a stupid question.
This can easily be achieved by making a class that processes that information:
class ProcessFooInformation(object):
def __init__(self, request):
self.request = request
#property
def bar(self):
baz = self.request.GET.get('baz', '')
# do something cool to baz and store it in foobar
return foobar
# etc...
Then using old style function views or new class-based views:
def my_view(request):
foo = ProcessFooInformation(request)
# use foo in whatever way and return a response
return render(request, 'foobar.html', {'foo':foo})
I also made this more efficient by using lazy evaluation of properties.
I adapted ideas from the lazy property evaluation recipe and the comments to write a wrapper:
def lazy_prop(func):
def wrap(self, *args, **kwargs):
if not func.__name__ in self.__dict__:
self.__dict__[func.__name__] = func(self, *args, **kwargs)
return self.__dict__[func.__name__]
return property(wrap)
This evaluates the value of the wrapped method only once per instance and uses a stored value on subsequent calls. This is useful if the property evaluates slowly.