Permalink
Browse files

Add elasticutils.estestcase.ESTestCase

This makes the ESTestCase "public". Previously, it was intertwined with
our test suite.

Further, it adds documentation for it to the api.

In the process of doing that, I nixed a bunch of skip_tests stuff. If
someone doesn't want it to run tests, they should wrap the class on
their own. That's a lot easier than us providing scaffolding that is
either generic (hard) or requires a specific test framework
(pain-in-the-ass-for-users).

Fixes #215
  • Loading branch information...
1 parent 5801280 commit 69e99df9ea9cbf54e0dadcd15872b6c2f98b1815 @willkg willkg committed Mar 24, 2014
View
@@ -146,6 +146,13 @@ The MLT class
.. automethod:: elasticutils.MLT.to_python
+The ESTestCase class
+====================
+
+.. autoclass:: elasticutils.estestcase.ESTestCase
+ :members:
+
+
Helper utilites
===============
@@ -4,7 +4,7 @@
from django.test import TestCase
from django.conf import settings
-from elasticsearch.exceptions import ConnectionError, NotFoundError
+from elasticsearch.exceptions import ConnectionError
from elasticsearch.helpers import bulk_index
# Try really really hard to find a valid skip thing.
@@ -0,0 +1,149 @@
+from unittest import TestCase
+
+from elasticsearch.helpers import bulk_index
+try:
+ from nose import SkipTest
+except ImportError:
+ try:
+ from unittest.case import SkipTest
+ except ImportError:
+ class SkipTest(Exception):
+ pass
+
+from elasticutils import get_es, S
+
+
+class ESTestCase(TestCase):
+ """Superclass for Elasticsearch-using test cases.
+
+ :property es_settings: settings to use to build a elasticsearch
+ Elasticsearch object
+ :property index_name: Name of the index to use for theses tests
+ :property mapping_type_name: The mapping type name for the mapping
+ you want created
+ :property mapping: The mapping to use when creating the index
+ :property data: Any documents to index during ``setup_class()``
+
+ For examples of usage, see ``tests/test_*.py`` files.
+
+ You probably want to subclass this and at least set relevant class
+ properties. Then use that subclass as the superclass for your
+ tests.
+
+ """
+ index_name = 'elasticutilstest'
+ mapping_type_name = 'elasticutilsmappingtype'
+ es_settings = {
+ 'urls': ['http://localhost:9200']
+ }
+ mapping = {}
+ data = []
+
+ @classmethod
+ def setup_class(cls):
+ """Sets up the index specified by ``cls.index_name``
+
+ This will create the index named ``cls.index_name`` with the
+ mapping specified in ``cls.mapping`` and indexes any data
+ specified in ``cls.data``.
+
+ If you need something different, then override this.
+
+ """
+ # Note: TestCase has no setup_class, so we don't call super()
+ # here.
+ cls.cleanup_index()
+ cls.create_index(settings={'mappings': cls.mapping})
+ if cls.data:
+ cls.index_data(cls.data)
+ cls.refresh()
+
+ @classmethod
+ def teardown_class(cls):
+ """Removes the index specified by ``cls.index_name``
+
+ This should clean up anything created in ``cls.setup_class()``
+ and anything created by the tests.
+
+ """
+ cls.cleanup_index()
+
+ def shortDescription(self):
+ # Prevent the docstring being used as the test name because
+ # that's irritating as all hell when trying to fix tests.
+ pass
+
+ @classmethod
+ def get_es(cls):
+ """Returns the Elasticsearch object specified by ``cls.es_settings``"""
+ return get_es(**cls.es_settings)
+
+ @classmethod
+ def get_s(cls, mapping_type=None):
+ """Returns an S for the settings on this class
+
+ Uses ``cls.es_settings`` to configure the Elasticsearch
+ object. Uses ``cls.index_name`` for the index and
+ ``cls.mapping_type_name`` for the MappingType to search.
+
+ :arg mapping_type: The MappingType class to use to create the S
+
+ """
+ if mapping_type is not None:
+ s = S(mapping_type)
+ else:
+ s = S()
+ return (s.es(**cls.es_settings)
+ .indexes(cls.index_name)
+ .doctypes(cls.mapping_type_name))
+
+ @classmethod
+ def create_index(cls, settings=None):
+ """Creates an index with specified settings
+
+ Uses ``cls.index_name`` as the index to create.
+
+ :arg settings: Any additional settings to use to create the
+ index.
+
+ """
+ body = {}
+ if settings:
+ body['settings'] = settings
+ cls.get_es().indices.create(index=cls.index_name, body=body)
+
+ @classmethod
+ def index_data(cls, documents, id_field='id'):
+ """Indexes specified data
+
+ Uses ``cls.index_name`` as the index to index into. Uses
+ ``cls.mapping_type_name`` as the doctype to index these
+ documents as.
+
+ :arg documents: List of documents as Python dicts
+ :arg id_field: The field of the document that represents the id
+
+ """
+ documents = (dict(d, _id=d[id_field]) for d in documents)
+ bulk_index(cls.get_es(), documents, index=cls.index_name,
+ doc_type=cls.mapping_type_name)
+ cls.refresh()
+
+ @classmethod
+ def cleanup_index(cls):
+ """Cleans up the index
+
+ This deletes the index named by ``cls.index_name``.
+
+ """
+ cls.get_es().indices.delete(index=cls.index_name, ignore=404)
+
+ @classmethod
+ def refresh(cls):
+ """Refresh index after indexing
+
+ This refreshes the index specified by ``cls.index_name``.
+
+ """
+ cls.get_es().indices.refresh(index=cls.index_name)
+ cls.get_es().cluster.health(wait_for_status='yellow')
@@ -1,136 +1,9 @@
-import time
from distutils.version import LooseVersion
from functools import wraps
-from unittest import TestCase
from nose import SkipTest
-import elasticsearch
-from elasticutils import get_es, S
-
-
-class ESTestCase(TestCase):
- """Superclass for Elasticsearch-using test cases.
-
- :property index_name: name of the index to use
- :property mapping_type_name: the mapping type name
- :property es_settings: settings to use to build a elasticsearch
- Elasticsearch object.
- :property mapping: the mapping to use when creating an index
- :property data: any data to add to the index in setup_class
- :property skip_tests: if Elasticsearch isn't available, then this
- is True and therefore tests should be skipped for this class
-
- For examples of usage, see the other ``test_*.py`` files.
-
- """
- index_name = 'elasticutilstest'
- mapping_type_name = 'elasticutilsmappingtype'
- es_settings = {
- 'urls': ['http://localhost:9200']
- }
- mapping = {}
- data = []
- skip_tests = False
-
- @classmethod
- def setup_class(cls):
- """Class setup for tests.
-
- Checks to see if ES is running and if not, sets ``skip_test``
- to True on the class.
- """
- # Note: TestCase has no setup_class
- try:
- get_es().cluster.health()
- except elasticsearch.TransportError:
- cls.skip_tests = True
- return
-
- if cls.data:
- cls.create_index(settings={'mappings': cls.mapping})
- cls.index_data(cls.data)
- cls.refresh()
-
- @classmethod
- def teardown_class(cls):
- """Class tear down for tests."""
- if cls.skip_tests:
- return
-
- cls.cleanup_index()
-
- def setUp(self):
- """Set up a single test.
-
- :raises SkipTest: if ``skip_tests`` is True for this
- class/instance
- """
- if self.skip_tests:
- raise SkipTest
- super(ESTestCase, self).setUp()
-
- def shortDescription(self):
- # Prevent the docstring being used as the test name because
- # that's irritating as all hell when trying to fix tests.
- pass
-
- @classmethod
- def get_es(cls):
- return get_es(**cls.es_settings)
-
- @classmethod
- def get_s(cls, mapping_type=None):
- if mapping_type is not None:
- s = S(mapping_type)
- else:
- s = S()
- return (s.es(**cls.es_settings)
- .indexes(cls.index_name)
- .doctypes(cls.mapping_type_name))
-
- @classmethod
- def create_index(cls, settings=None):
- es = cls.get_es()
- try:
- es.indices.delete(cls.index_name)
- except elasticsearch.NotFoundError:
- pass
- body = {}
- if settings:
- body['settings'] = settings
- es.indices.create(cls.index_name, body=body)
-
- @classmethod
- def index_data(cls, data, index=None, doctype=None):
- index = index or cls.index_name
- doctype = doctype or cls.mapping_type_name
-
- es = cls.get_es()
-
- # TODO: change this to a bulk index
- for item in data:
- es.index(index, doctype, item, id=item['id'])
-
- cls.refresh()
-
- @classmethod
- def cleanup_index(cls):
- es = cls.get_es()
- try:
- es.indices.delete(cls.index_name)
- except elasticsearch.NotFoundError:
- pass
-
- @classmethod
- def refresh(cls):
- """Refresh index after indexing.
-
- This refreshes the index specified by `self.index_name`.
-
- """
- cls.get_es().indices.refresh(cls.index_name)
- cls.get_es().cluster.health(wait_for_status='yellow')
+from elasticutils.estestcase import ESTestCase # noqa
def facet_counts_dict(qs, field):
@@ -5,23 +5,15 @@
class MoreLikeThisTest(ESTestCase):
- @classmethod
- def setup_class(cls):
- super(MoreLikeThisTest, cls).setup_class()
- if cls.skip_tests:
- return
-
- cls.create_index()
- cls.index_data([
- {'id': 1, 'foo': 'bar', 'tag': 'awesome'},
- {'id': 2, 'foo': 'bar', 'tag': 'boring'},
- {'id': 3, 'foo': 'bar', 'tag': 'awesome'},
- {'id': 4, 'foo': 'bar', 'tag': 'boring'},
- {'id': 5, 'foo': 'bar', 'tag': 'elite'},
- {'id': 6, 'foo': 'notbar', 'tag': 'gross'},
- {'id': 7, 'foo': 'notbar', 'tag': 'awesome'},
- ])
- cls.refresh()
+ data = [
+ {'id': 1, 'foo': 'bar', 'tag': 'awesome'},
+ {'id': 2, 'foo': 'bar', 'tag': 'boring'},
+ {'id': 3, 'foo': 'bar', 'tag': 'awesome'},
+ {'id': 4, 'foo': 'bar', 'tag': 'boring'},
+ {'id': 5, 'foo': 'bar', 'tag': 'elite'},
+ {'id': 6, 'foo': 'notbar', 'tag': 'gross'},
+ {'id': 7, 'foo': 'notbar', 'tag': 'awesome'},
+ ]
def test_bad_mlt(self):
"""Tests S or index and doc_type is specified."""
Oops, something went wrong.

0 comments on commit 69e99df

Please sign in to comment.