How to start a server from the command line in a Python unittest
python, subprocess, unit-testing
Solution
The problem is as you said, that `subprocess.call` is blocking and will wait for the command to finish. For scenarios where you need to spawn a child process without waiting for it to finish, you can use `subprocess.Popen`:
process = subprocess.Popen(["rethinkdb", "--port-offset", str(TestController.PORT_OFFSET)])
This gives you back a `Popen` object that provides a whole bunch of very useful methods to communicate with the child process. For example, you will probably want to use `process.kill()` in your unit test's `tearDown()` function to shut down your database.
Problem
I'm building a tester for a Python script which performs works with a RethinkDB database. As part of the `setUp()` method, I'm trying to make the tester start up the RethinkDB server on `localhost` on port `28016` in case that has not yet been done. I'm using `subprocess` to start the server. The problem is that, according to https://docs.python.org/2/library/subprocess.html, `subprocess` waits for the command to complete. In this case, it seems that as long as the server is up and running, the process is not complete and testing does not continue beyond the `setUp()` stage. Here is the script I'm trying: ``` import unittest import rethinkdb as r import subprocess class TestController(unittest.TestCase): HOST = "localhost" PORT_OFFSET = 1 PORT = 28015 + PORT_OFFSET DB = "ipercron" TABLE = "sensor_data" def setUp(self): try: self.conn = r.connect(self.HOST, self.PORT) except r.ReqlDriverError: print("The RethinkDB server is not yet ready. Starting it up...") subprocess.call(["rethinkdb", "--port-offset", str(TestController.PORT_OFFSET)]) self.conn = r.connect(self.HOST, self.PORT) if TestController.DB not in r.db_list().run(self.conn): r.db_create(TestController.DB).run(self.conn) self.conn.use(TestController.DB) if TestController.TABLE not in r.table_list().run(self.conn): r.table_create(TestController.TABLE).run(self.conn) # Create the table if it does not yet exist r.table(TestController.TABLE).delete().run(self.conn) # Empty the table to start with a clean slate def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') suite = unittest.TestLoader().loadTestsFromTestCase(TestController) unittest.TextTestRunner(verbosity=2).run(suite) ``` The `subprocess` is meant to perform the `rethinkdb --port-offset 1` command at the command line and then continue with the script. However, when I run the script I get the usual message that the server is ready: ``` kurt@kurt-ThinkPad:~/dev/clones/ipercron-compose/controller$ python unittest_controller.py test_upper (__main__.TestController) ... The RethinkDB server is not yet ready. Starting it up... Running rethinkdb 2.3.5~0xenial (GCC 5.3.1)... Running on Linux 4.4.0-42-generic x86_64 Loading data from directory /home/kurt/dev/clones/ipercron-compose/controller/rethinkdb_data Listening for intracluster connections on port 29016 Listening for client driver connections on port 28016 Listening for administrative HTTP connections on port 8081 Listening on cluster addresses: 127.0.0.1, 127.0.1.1, ::1 Listening on driver addresses: 127.0.0.1, 127.0.1.1, ::1 Listening on http addresses: 127.0.0.1, 127.0.1.1, ::1 To fully expose RethinkDB on the network, bind to all addresses by running rethinkdb with the `--bind all` command line option. Server ready, "kurt_ThinkPad_a0k" 07bb35f6-3a33-4e8b-9e9c-a78504457969 ``` Without any further action. How can I make the unittest proceed with the testing?