diff --git a/.gitignore b/.gitignore
index f75ca9b..5a13495 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,6 @@
 !.gitignore
 !.travis.yml
 dist/
+*.db
 *.egg-info/
+*.pyc
diff --git a/.travis.yml b/.travis.yml
index f097728..a20df42 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,20 @@
 language: python
-python: '3.4'
+python:
+- '2.7'
+- '3.4'
 branches:
   except: "/^v\\d/"
-install: true
-script: true
+services:
+- mysql
+- postgresql
+install:
+- python setup.py install
+- pip install mysqlclient
+- pip install psycopg2
+before_script:
+- mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
+- psql -c 'create database test;' -U postgres
+script: python tests/sqltests.py
 deploy:
 - provider: script
   script: 'curl --fail --data "{ \"tag_name\": \"v$(python setup.py --version)\",
diff --git a/setup.py b/setup.py
index 2342a33..6b7fce7 100644
--- a/setup.py
+++ b/setup.py
@@ -13,6 +13,7 @@
     install_requires=["SQLAlchemy"],
     keywords="cs50",
     name="cs50",
+    package_dir={"": "src"},
     packages=["cs50"],
     url="https://github.com/cs50/python-cs50",
     version="2.2.0"
diff --git a/src/cs50/__pycache__/__init__.cpython-34.pyc b/src/cs50/__pycache__/__init__.cpython-34.pyc
deleted file mode 100644
index c4a23a2..0000000
Binary files a/src/cs50/__pycache__/__init__.cpython-34.pyc and /dev/null differ
diff --git a/src/cs50/__pycache__/__init__.cpython-36.pyc b/src/cs50/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index 7eb8ca4..0000000
Binary files a/src/cs50/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/src/cs50/__pycache__/cs50.cpython-34.pyc b/src/cs50/__pycache__/cs50.cpython-34.pyc
deleted file mode 100644
index dd8d33b..0000000
Binary files a/src/cs50/__pycache__/cs50.cpython-34.pyc and /dev/null differ
diff --git a/src/cs50/__pycache__/cs50.cpython-36.pyc b/src/cs50/__pycache__/cs50.cpython-36.pyc
deleted file mode 100644
index b444481..0000000
Binary files a/src/cs50/__pycache__/cs50.cpython-36.pyc and /dev/null differ
diff --git a/src/cs50/__pycache__/sql.cpython-34.pyc b/src/cs50/__pycache__/sql.cpython-34.pyc
deleted file mode 100644
index 8e56731..0000000
Binary files a/src/cs50/__pycache__/sql.cpython-34.pyc and /dev/null differ
diff --git a/tests/sqltests.py b/tests/sqltests.py
index d2204a1..8e518b4 100644
--- a/tests/sqltests.py
+++ b/tests/sqltests.py
@@ -1,7 +1,12 @@
-import unittest
 from cs50.sql import SQL
+import sys
+import unittest
+import warnings
 
 class SQLTests(unittest.TestCase):
+    def multi_inserts_enabled(self):
+        return True
+
     def test_delete_returns_affected_rows(self):
         rows = [
             {"id": 1, "val": "foo"},
@@ -22,6 +27,8 @@ def test_delete_returns_affected_rows(self):
     def test_insert_returns_last_row_id(self):
         self.assertEqual(self.db.execute("INSERT INTO cs50(val) VALUES('foo')"), 1)
         self.assertEqual(self.db.execute("INSERT INTO cs50(val) VALUES('bar')"), 2)
+        if self.multi_inserts_enabled():
+            self.assertEqual(self.db.execute("INSERT INTO cs50(val) VALUES('baz'); INSERT INTO cs50(val) VALUES('qux')"), 4)
 
     def test_select_all(self):
         self.assertEqual(self.db.execute("SELECT * FROM cs50"), [])
@@ -70,54 +77,44 @@ def test_update_returns_affected_rows(self):
         self.assertEqual(self.db.execute("UPDATE cs50 SET val = 'foo' WHERE id > 1"), 2)
         self.assertEqual(self.db.execute("UPDATE cs50 SET val = 'foo' WHERE id = -50"), 0)
 
-class MySQLTests(SQLTests):
-    @classmethod
-    def setUpClass(self):
-        self.db = SQL("mysql://root@localhost/cs50_sql_tests")
-
-    def setUp(self):
-        self.db.execute("CREATE TABLE cs50 (id INTEGER NOT NULL AUTO_INCREMENT, val VARCHAR(16), PRIMARY KEY (id))")
-
     def tearDown(self):
         self.db.execute("DROP TABLE cs50")
 
     @classmethod
     def tearDownClass(self):
-        self.db.execute("DROP TABLE IF EXISTS cs50")
+        try:
+            self.db.execute("DROP TABLE IF EXISTS cs50")
+        except Warning as e:
+            # suppress "unknown table"
+            if not str(e).startswith("(1051"):
+                raise e
 
-class PostgresTests(SQLTests):
+class MySQLTests(SQLTests):
     @classmethod
     def setUpClass(self):
-        self.db = SQL("postgresql://postgres:postgres@localhost/cs50_sql_tests")
+        self.db = SQL("mysql://root@localhost/test")
 
     def setUp(self):
-        self.db.execute("CREATE TABLE cs50 (id SERIAL PRIMARY KEY, val VARCHAR(16))")
-
-    def tearDown(self):
-        self.db.execute("DROP TABLE cs50")
+        self.db.execute("CREATE TABLE cs50 (id INTEGER NOT NULL AUTO_INCREMENT, val VARCHAR(16), PRIMARY KEY (id))")
 
+class PostgresTests(SQLTests):
     @classmethod
-    def tearDownClass(self):
-        self.db.execute("DROP TABLE IF EXISTS cs50")
+    def setUpClass(self):
+        self.db = SQL("postgresql://postgres@localhost/test")
 
-    def test_insert_returns_last_row_id(self):
-        self.assertEqual(self.db.execute("INSERT INTO cs50(val) VALUES('foo')"), 1)
-        self.assertEqual(self.db.execute("INSERT INTO cs50(val) VALUES('bar')"), 2)
+    def setUp(self):
+        self.db.execute("CREATE TABLE cs50 (id SERIAL PRIMARY KEY, val VARCHAR(16))")
 
 class SQLiteTests(SQLTests):
     @classmethod
     def setUpClass(self):
-        self.db = SQL("sqlite:///cs50_sql_tests.db")
+        self.db = SQL("sqlite:///test.db")
 
     def setUp(self):
         self.db.execute("CREATE TABLE cs50(id INTEGER PRIMARY KEY, val TEXT)")
 
-    def tearDown(self):
-        self.db.execute("DROP TABLE cs50")
-
-    @classmethod
-    def tearDownClass(self):
-        self.db.execute("DROP TABLE IF EXISTS cs50")
+    def multi_inserts_enabled(self):
+        return False
 
 if __name__ == "__main__":
     suite = unittest.TestSuite([
@@ -126,4 +123,4 @@ def tearDownClass(self):
         unittest.TestLoader().loadTestsFromTestCase(PostgresTests)
     ])
 
-    unittest.TextTestRunner(verbosity=2).run(suite)
+    sys.exit(not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful())