Elasticmock does not mock the Elasticsearch client
Opened this issue · 1 comments
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.
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.