Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Elasticmock does not mock the Elasticsearch client #82

Open
wpm opened this issue Jan 14, 2023 · 1 comment
Open

Elasticmock does not mock the Elasticsearch client #82

wpm opened this issue Jan 14, 2023 · 1 comment

Comments

@wpm
Copy link

wpm commented Jan 14, 2023

I have written the following unit test.

from unittest import TestCase

from elasticmock import elasticmock
from elasticsearch import Elasticsearch


def some_function_that_uses_elasticsearch():
    client = Elasticsearch(hosts=[{"host": "localhost", "port": 9200}])
    id = client.index("test-index", {"a": "b"})
    print(id)
    return True


class TestClass(TestCase):
    @elasticmock
    def test_should_return_something_from_elasticsearch(self):
        self.assertIsNotNone(some_function_that_uses_elasticsearch())

If I run the test without a local instance of Elasticsearch running I get the following error.

elasticsearch.exceptions.ConnectionError: ConnectionError(<urllib3.connection.HTTPConnection object at 0x7ff720769370>: 
Failed to establish a new connection: [Errno 61] Connection refused) caused by: 
NewConnectionError(<urllib3.connection.HTTPConnection object at 0x7ff720769370>:
Failed to establish a new connection: 
[Errno 61] Connection refused)

If I step through in the debugger I see that the client is an ElasticSearch object and not a FakeElasticSearch object. Apparently the @elasticmock decorator has no effect.

I have the same problem with the pytest framework.

from elasticmock import elasticmock
from elasticsearch import Elasticsearch


@elasticmock
def test_mocked_elasticsearch_client():
    client = Elasticsearch(hosts=[{"host": "localhost", "port": 9200}])
    id = client.index("test-index", {"a": "b"})
    print(id)

The "Code Example" with FooService on the project homepage does work. But I can't figure out what is wrong with the variants I have here.

This is elasticmock 1.8.1 and elasticsearch 7.13.3 on OS X with Python 3.9.15.

@sillly-milly
Copy link

I believe this is due to how @elasticmock is patching ES and how you're importing the client class.
@elasticmock uses unittest.mock.patch to essentially do setattr(elasticsearch, 'Elasticsearch', <mock_class>), so that any future getattr(elasticsearch, 'Elasticsearch') will return the mocked class. This happens at test function call time.
Before this happens, you already imported the (original) ElasticSearch class from the (unpatched) elasticsearch module into your namespace, and setattr does not work in-place so your namespace retains the original class reference, which your test function then uses.

Workarounds will involve ensuring you do your getattr on the elasticsearch module after elasticmock has done setattr a.k.a. inside your test function:
a) Move the elasticsearch import inside the test function.
b) Change your top-level import to be import elasticsearch, then access the Elasticsearch class inside the test function with elasticsearch.Elasticsearch (this is what I did for my use-case).

I don't know what a good prevention measure would be for this. I don't think elasticmock can see the Elasticsearch class reference in your local namespace, so it can't patch it for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants