How to call an external function without sending the 'self'?

Asked

Viewed 6,715 times

21

I’m using a class attribute to save the View that I want to test on a Django application, like this:

# TESTS.PY
class OrderTests(TestCase, ShopTest):
    _VIEW = views.order

    def test_gateway_answer(self):
        url = 'whatever url'
        request = self.request_factory(url, 'GET')
        self._VIEW(request, **{'sku': order.sku})


# VIEWS.PY
def order(request, sku)
     ...

But during execution, as I call an attribute of my object OrderTests python sends self as argument, which does not match the signature of the function order and causes all kinds of problems.

Is there any way to make Python not send self in that case?

4 answers

9


The ideal is to use the decorator staticmethod Python in attribute declaration _VIEW. After all, what you want is to access it statically (independent of the test class instance OrderTests).

# TESTS.PY
class OrderTests(TestCase, ShopTest):
    _VIEW = staticmethod(views.order)

    def test_gateway_answer(self):
        url = 'whatever url'
        request = self.request_factory(url, 'GET')
        self._VIEW(request, **{'sku': order.sku})


# VIEWS.PY
def order(request, sku)
     ...

7

Note: this response indicates an alternative way of doing unbinding in a method bounded, which may be useful in some situations, but there are better ways to solve the original question. See the answer accepted for a solution preferable to this.

You can use the property __func__ of his method (im_func, if Python <2.6):

>>> OrderTests()._VIEW
<bound method OrderTests.order of <__main__.OrderTests object at 0x00BFA090>>
>>> OrderTests()._VIEW.__func__
<function order at 0x00BFE170>

When a method is accessed through the syntax objeto.método the result is a bound method, which is already connected to the source object (i.e. it assigns the self for you, and you call him by passing one less positional argument). Calling yourself __func__ you have a reference to the original function, so that it is necessary to pass the self explicitly. (when there is a self, of course - since this is only a convention; in your case, the first argument to order is, in fact, the very request)

self._VIEW.__func__(request, **{'sku': order.sku})

Source (in English) here, search for "User-defined methods".

  • 1

    That works, but it’s far from the right way. If you have bothered to put a member of the class because you want to reuse it in several test cases, it needs to be a generic callable. If it is an object you have to call the attribute __func__, and tomorrow that _VIEW is switched to something else, has to touch the methods.

  • @jsbueno I agree. I left a note at the beginning of the reply clarifying this point.

3

In Python you can call a method or property from within a two-mode class, but first consider the following class:

class Pessoa:
    def __init__(self, nome = "nenhum"):
        self.nome = nome

    def set_nome(self, nome):
        self.nome = nome

    def get_nome(self):
        return self.nome

- Passing the object to the class when calling the method:

amigos = [Pessoa("Lucas"), Pessoa("Julia"), Pessoa("Bruna")]

for amigo in amigos:
    print(Pessoa.get_nome(amigo))

In this method you use the class to call the methods and properties, but the first argument must be an object of this class, as it needs to take this information from somewhere. I usually use this method when I have a list of objects of the same type, because it facilitates access to methods and properties, apparently when reading your question I had the impression that you used this form.

- Calling the method through the object:

maria = Pessoa("Maria")

print(maria.get_nome())

This method is the most widely used and is very similar to what happens in object-oriented languages like Java and C#. So that "Python does not send self" as described in your question, you need to use this method. Just a correction, you said that order is a function, if order is asking as a first argument self means that it is contained in a class, so it is not called "function" but "method".

  • 1

    I think you misunderstood the question... order is not an object method, it is in fact an isolated function. The problem is that the reference to this function is stored in a class, so that the call from it (by means of the second form presented in your reply) is incorrectly assigning the self, when what OP wants is that it doesn’t happen. See my answer to a possible solution (there may be others)

1

You can just wrapper the function you want to test.

# TESTS.PY
class OrderTests(TestCase, ShopTest):
    def _VIEW(self, *args, **kwargs):
        views.order(*args, **kwargs)

    def test_gateway_answer(self):
        url = 'whatever url'
        request = self.request_factory(url, 'GET')
        self._VIEW(request, **{'sku': order.sku})


# VIEWS.PY
def order(request, sku)
     ...

It’s an extra line of code, but it makes it very clear what you’re doing.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.