Django testing of neo4j database
Asked Answered
F

3

1

I'm using django with neo4j as database and noemodel as OGM. How do I test it?

When I run python3 manage.py test all the changes, my tests make are left.

And also how do I make two databases, one for testing, another for working in production and specify which one to use how?

Frottage answered 8/8, 2015 at 16:5 Comment(1)
can you put your django's settings file, which package you used, i am struggling to connect neo4j with django, any resource to take a look at !Pluralism
L
3

I assume the reason all of your changes are being retained is due to using the same neo4j database for testing as you are using in development. Since neomodel isn't integrated tightly with Django it doesn't act the same way Django's ORM does when testing. Django will do some helpful things when you run tests using its ORM, such as creating a test database that will be destroyed upon completion.

With neo4j and neomodel I'd recommend doing the following:

Create a Custom Test Runner

Django enables you to define a custom test runner by setting the TEST_RUNNER settings variable. An extremely simple version of this to get you going would be:

from time import sleep
from subprocess import call

from django.test.runner import DiscoverRunner


class MyTestRunner(DiscoverRunner):
    def setup_databases(self, *args, **kwargs):
        # Stop your development instance
        call("sudo service neo4j-service stop", shell=True)
        # Sleep to ensure the service has completely stopped
        sleep(1)
        # Start your test instance (see section below for more details)
        success = call("/path/to/test/db/neo4j-community-2.2.2/bin/neo4j"
                       " start-no-wait", shell=True)
        # Need to sleep to wait for the test instance to completely come up
        sleep(10)
        if success != 0:
            return False
        try:
            # For neo4j 2.2.x you'll need to set a password or deactivate auth
            # Nigel Small's py2neo gives us an easy way to accomplish this
            call("source /path/to/virtualenv/bin/activate && "
                 "/path/to/virtualenv/bin/neoauth "
                 "neo4j neo4j my-p4ssword")
        except OSError:
            pass
        # Don't import neomodel until we get here because we need to wait 
        # for the new db to be spawned
        from neomodel import db
        # Delete all previous entries in the db prior to running tests
        query = "match (n)-[r]-() delete n,r"
        db.cypher_query(query)
        super(MyTestRunner, self).__init__(*args, **kwargs)

    def teardown_databases(self, old_config, **kwargs):
        from neomodel import db
        # Delete all previous entries in the db after running tests
        query = "match (n)-[r]-() delete n,r"
        db.cypher_query(query)
        sleep(1)
        # Shut down test neo4j instance
        success = call("/path/to/test/db/neo4j-community-2.2.2/bin/neo4j"
                       " stop", shell=True)
        if success != 0:
            return False
        sleep(1)
        # start back up development instance
        call("sudo service neo4j-service start", shell=True)

Add a secondary neo4j database

This can be done in a couple ways but to follow along with the test runner above you can download a community distribution from neo4j's website. With this secondary instance you can now swap between which database you'd like to use utilizing the command line statements used in the calls within the test runner.

Wrap Up

This solution assume's you're on a linux box but should be portable to a different OS with minor modifications. Also I'd recommend checking out the Django's Test Runner Docs to expand upon what the test runner can do.

Laurustinus answered 10/8, 2015 at 14:49 Comment(0)
S
2

There currently isn't mechanism for working with test databases in neomodel as neo4j only has 1 schema per instance.

However you can override the environment variable NEO4J_REST_URL when running the tests like so

export NEO4J_REST_URL=http://localhost:7473/db/data python3 manage.py test

Spaceless answered 10/8, 2015 at 15:0 Comment(0)
E
0

The way I went about this was to give in and use the existing database, but mark all test-related nodes and detach/delete them when finished. It's obviously not ideal; all your node classes must inherit from NodeBase or risk polluting the db with test data, and if you have unique constraints, those will still be enforced across both live/test data. But it works for my purposes, and I thought I'd share in case it helps someone else.


in myproject/base.py:

from neomodel.properties import Property, validator
from django.conf import settings


class TestModeProperty(Property):
    """
    Boolean property that is only set during unit testing.
    """

    @validator
    def inflate(self, value):
        return bool(value)

    @validator
    def deflate(self, value):
        return bool(value)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.default = True
        self.has_default = settings.UNIT_TESTING


class NodeBase(StructuredNode):
    __abstract_node__ = True
    test_mode = TestModeProperty()

in myproject/test_runner.py:

from django.test.runner import DiscoverRunner
from neomodel import db


class NeoDiscoverRunner(DiscoverRunner):

    def teardown_databases(self, old_config, **kwargs):
        db.cypher_query(
            """
            MATCH (node {test_mode: true})
            DETACH DELETE node
            """
        )
        return super().teardown_databases(old_config, **kwargs)

in settings.py:

UNIT_TESTING = sys.argv[1:2] == ["test"]
TEST_RUNNER = "myproject.test_runner.NeoDiscoverRunner"
Edrei answered 1/9, 2022 at 4:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.