diff --git a/metobsapi/tests/test_data_api.py b/metobsapi/tests/test_data_api.py
index c6ff10d9e7bbcebb6ead47086a3e50bc31e3fd0b..2b8fa9b81c83467fe6bb27d258b8a6322f6c29cf 100644
--- a/metobsapi/tests/test_data_api.py
+++ b/metobsapi/tests/test_data_api.py
@@ -1,8 +1,7 @@
 import json
-import unittest
 from unittest import mock
 
-import metobsapi
+import pytest
 
 
 def fake_data(interval, symbols, num_vals, single_result=False):
@@ -53,121 +52,129 @@ def fake_data(interval, symbols, num_vals, single_result=False):
         return series
 
 
-class TestDataAPI(unittest.TestCase):
-    def setUp(self):
-        metobsapi.app.config["TESTING"] = True
-        metobsapi.app.config["DEBUG"] = True
-        self.app = metobsapi.app.test_client()
+@pytest.fixture
+def app():
+    from metobsapi import app as real_app
 
-    def test_doc(self):
-        res = self.app.get("/api/data")
+    real_app.config.update({"TESTING": True, "DEBUG": True})
+    yield real_app
+
+
+@pytest.fixture
+def client(app):
+    return app.test_client()
+
+
+class TestDataAPI:
+    def test_doc(self, client):
+        res = client.get("/api/data")
         assert b"Data Request Application" in res.data
 
-    def test_bad_format(self):
-        res = self.app.get("/api/data.fake")
-        self.assertIn(b"No data file format", res.data)
+    def test_bad_format(self, client):
+        res = client.get("/api/data.fake")
+        assert b"No data file format" in res.data
 
-    def test_bad_begin_json(self):
-        res = self.app.get("/api/data.json?symbols=air_temp&begin=blah")
+    def test_bad_begin_json(self, client):
+        res = client.get("/api/data.json?symbols=air_temp&begin=blah")
         res = json.loads(res.data.decode())
-        self.assertEqual(res["code"], 400)
-        self.assertEqual(res["status"], "error")
-        self.assertIn("timestamp", res["message"])
+        assert res["code"] == 400
+        assert res["status"] == "error"
+        assert "timestamp" in res["message"]
 
-    def test_bad_order(self):
-        res = self.app.get("/api/data.json?order=blah&symbols=air_temp")
+    def test_bad_order(self, client):
+        res = client.get("/api/data.json?order=blah&symbols=air_temp")
         res = json.loads(res.data.decode())
-        self.assertIn("column", res["message"])
-        self.assertIn("row", res["message"])
+        assert "column" in res["message"]
+        assert "row" in res["message"]
 
-    def test_bad_epoch(self):
-        res = self.app.get("/api/data.json?epoch=blah&symbols=air_temp")
+    def test_bad_epoch(self, client):
+        res = client.get("/api/data.json?epoch=blah&symbols=air_temp")
         res = json.loads(res.data.decode())
-        self.assertIn("'h'", res["message"])
-        self.assertIn("'m'", res["message"])
-        self.assertIn("'s'", res["message"])
-        self.assertIn("'u'", res["message"])
+        assert "'h'" in res["message"]
+        assert "'m'" in res["message"]
+        assert "'s'" in res["message"]
+        assert "'u'" in res["message"]
 
-    def test_bad_interval(self):
-        res = self.app.get("/api/data.json?interval=blah&symbols=air_temp")
+    def test_bad_interval(self, client):
+        res = client.get("/api/data.json?interval=blah&symbols=air_temp")
         res = json.loads(res.data.decode())
-        self.assertIn("'1m'", res["message"])
-        self.assertIn("'5m'", res["message"])
-        self.assertIn("'1h'", res["message"])
+        assert "'1m'" in res["message"]
+        assert "'5m'" in res["message"]
+        assert "'1h'" in res["message"]
 
-    def test_missing_inst(self):
-        res = self.app.get("/api/data.json?site=X&symbols=air_temp&begin=-05:00:00")
+    def test_missing_inst(self, client):
+        res = client.get("/api/data.json?site=X&symbols=air_temp&begin=-05:00:00")
         res = json.loads(res.data.decode())
-        self.assertEqual(res["code"], 400)
-        self.assertEqual(res["status"], "error")
-        self.assertIn("'site'", res["message"])
-        self.assertIn("'inst'", res["message"])
+        assert res["code"] == 400
+        assert res["status"] == "error"
+        assert "'site'" in res["message"]
+        assert "'inst'" in res["message"]
 
-    def test_missing_site(self):
-        res = self.app.get("/api/data.json?inst=X&symbols=air_temp&begin=-05:00:00")
+    def test_missing_site(self, client):
+        res = client.get("/api/data.json?inst=X&symbols=air_temp&begin=-05:00:00")
         res = json.loads(res.data.decode())
-        self.assertEqual(res["code"], 400)
-        self.assertEqual(res["status"], "error")
-        self.assertIn("'site'", res["message"])
-        self.assertIn("'inst'", res["message"])
+        assert res["code"] == 400
+        assert res["status"] == "error"
+        assert "'site'" in res["message"]
+        assert "'inst'" in res["message"]
 
-    def test_missing_symbols(self):
-        res = self.app.get("/api/data.json?begin=-05:00:00")
+    def test_missing_symbols(self, client):
+        res = client.get("/api/data.json?begin=-05:00:00")
         res = json.loads(res.data.decode())
-        self.assertEqual(res["code"], 400)
-        self.assertEqual(res["status"], "error")
-        self.assertIn("'symbols'", res["message"])
+        assert res["code"] == 400
+        assert res["status"] == "error"
+        assert "'symbols'" in res["message"]
 
-    def test_too_many_points(self):
-        res = self.app.get("/api/data.json?symbols=aoss.tower.air_temp&begin=1970-01-01T00:00:00")
-        self.assertEqual(res.status_code, 413)
+    def test_too_many_points(self, client):
+        res = client.get("/api/data.json?symbols=aoss.tower.air_temp&begin=1970-01-01T00:00:00")
+        assert res.status_code == 413
         res = json.loads(res.data.decode())
-        self.assertIn("too many values", res["message"])
-        self.assertEqual(res["code"], 413)
-        self.assertEqual(res["status"], "fail")
+        assert "too many values" in res["message"]
+        assert res["code"] == 413
+        assert res["status"] == "fail"
 
     @mock.patch("metobsapi.data_api.query")
-    def test_shorthand_one_symbol_json_row(self, query_func):
+    def test_shorthand_one_symbol_json_row(self, query_func, client):
         r = fake_data("1m", {("aoss", "tower"): ["time", "air_temp"]}, 9)
         query_func.return_value = r
         # row should be the default
-        res = self.app.get("/api/data.json?site=aoss&inst=tower&symbols=air_temp&begin=-00:10:00")
+        res = client.get("/api/data.json?site=aoss&inst=tower&symbols=air_temp&begin=-00:10:00")
         res = json.loads(res.data.decode())
-        self.assertEqual(res["code"], 200)
-        self.assertEqual(res["num_results"], 9)
-        self.assertListEqual(res["results"]["symbols"], ["air_temp"])
-        self.assertEqual(len(res["results"]["timestamps"]), 9)
-        self.assertEqual(len(res["results"]["data"]), 9)
-        self.assertEqual(len(res["results"]["data"][0]), 1)
+        assert res["code"] == 200
+        assert res["num_results"] == 9
+        assert res["results"]["symbols"] == ["air_temp"]
+        assert len(res["results"]["timestamps"]) == 9
+        assert len(res["results"]["data"]) == 9
+        assert len(res["results"]["data"][0]) == 1
 
     @mock.patch("metobsapi.data_api.query")
-    def test_shorthand_one_symbol_json_column(self, query_func):
+    def test_shorthand_one_symbol_json_column(self, query_func, client):
         r = fake_data("1m", {("aoss", "tower"): ["time", "air_temp"]}, 9)
         query_func.return_value = r
-        res = self.app.get("/api/data.json?site=aoss&inst=tower&symbols=air_temp&begin=-00:10:00&order=column")
+        res = client.get("/api/data.json?site=aoss&inst=tower&symbols=air_temp&begin=-00:10:00&order=column")
         res = json.loads(res.data.decode())
-        self.assertEqual(res["code"], 200)
-        self.assertEqual(res["num_results"], 9)
-        self.assertIn("air_temp", res["results"]["data"])
-        self.assertEqual(len(res["results"]["data"]["air_temp"]), 9)
-        self.assertEqual(len(res["results"]["timestamps"]), 9)
+        assert res["code"] == 200
+        assert res["num_results"] == 9
+        assert "air_temp" in res["results"]["data"]
+        assert len(res["results"]["data"]["air_temp"]) == 9
+        assert len(res["results"]["timestamps"]) == 9
 
     @mock.patch("metobsapi.data_api.query")
-    def test_wind_speed_direction_json(self, query_func):
+    def test_wind_speed_direction_json(self, query_func, client):
         r = fake_data("1m", {("aoss", "tower"): ["time", "wind_speed", "wind_direction", "wind_east", "wind_north"]}, 9)
         query_func.return_value = r
-        res = self.app.get(
+        res = client.get(
             "/api/data.json?symbols=aoss.tower.wind_speed:aoss.tower.wind_direction&begin=-00:10:00&order=column"
         )
         res = json.loads(res.data.decode())
-        self.assertEqual(res["code"], 200)
-        self.assertEqual(res["num_results"], 9)
-        self.assertIn("aoss.tower.wind_direction", res["results"]["data"])
-        self.assertIn("aoss.tower.wind_speed", res["results"]["data"])
-        self.assertEqual(len(list(res["results"]["data"].keys())), 2)
+        assert res["code"] == 200
+        assert res["num_results"] == 9
+        assert "aoss.tower.wind_direction" in res["results"]["data"]
+        assert "aoss.tower.wind_speed" in res["results"]["data"]
+        assert len(list(res["results"]["data"].keys())) == 2
 
     @mock.patch("metobsapi.data_api.query")
-    def test_one_symbol_two_insts_json_row(self, query_func):
+    def test_one_symbol_two_insts_json_row(self, query_func, client):
         r = fake_data(
             "1m",
             {
@@ -178,17 +185,17 @@ class TestDataAPI(unittest.TestCase):
         )
         query_func.return_value = r
         # row should be the default
-        res = self.app.get("/api/data.json?symbols=aoss.tower.air_temp:mendota.buoy.air_temp&begin=-00:10:00")
+        res = client.get("/api/data.json?symbols=aoss.tower.air_temp:mendota.buoy.air_temp&begin=-00:10:00")
         res = json.loads(res.data.decode())
-        self.assertEqual(res["code"], 200)
-        self.assertEqual(res["num_results"], 9)
-        self.assertListEqual(res["results"]["symbols"], ["aoss.tower.air_temp", "mendota.buoy.air_temp"])
-        self.assertEqual(len(res["results"]["timestamps"]), 9)
-        self.assertEqual(len(res["results"]["data"]), 9)
-        self.assertEqual(len(res["results"]["data"][0]), 2)
+        assert res["code"] == 200
+        assert res["num_results"] == 9
+        assert res["results"]["symbols"] == ["aoss.tower.air_temp", "mendota.buoy.air_temp"]
+        assert len(res["results"]["timestamps"]) == 9
+        assert len(res["results"]["data"]) == 9
+        assert len(res["results"]["data"][0]) == 2
 
     @mock.patch("metobsapi.data_api.query")
-    def test_one_symbol_three_insts_json_row(self, query_func):
+    def test_one_symbol_three_insts_json_row(self, query_func, client):
         r = fake_data(
             "1m",
             {
@@ -207,89 +214,87 @@ class TestDataAPI(unittest.TestCase):
         st[("site2", "inst2")] = st[("aoss", "tower")]
         st[("site3", "inst3")] = st[("aoss", "tower")]
         with mock.patch("metobsapi.util.data_responses.SYMBOL_TRANSLATIONS", st):
-            res = self.app.get(
+            res = client.get(
                 "/api/data.json?symbols=site1.inst1.air_temp:site2.inst2.air_temp:site3.inst3.air_temp&begin=-00:10:00"
             )
             res = json.loads(res.data.decode())
-            self.assertEqual(res["code"], 200)
-            self.assertEqual(res["num_results"], 9)
-            self.assertListEqual(
-                res["results"]["symbols"], ["site1.inst1.air_temp", "site2.inst2.air_temp", "site3.inst3.air_temp"]
-            )
-            self.assertEqual(len(res["results"]["timestamps"]), 9)
-            self.assertEqual(len(res["results"]["data"]), 9)
-            self.assertEqual(len(res["results"]["data"][0]), 3)
+            assert res["code"] == 200
+            assert res["num_results"] == 9
+            assert res["results"]["symbols"] == ["site1.inst1.air_temp", "site2.inst2.air_temp", "site3.inst3.air_temp"]
+            assert len(res["results"]["timestamps"]) == 9
+            assert len(res["results"]["data"]) == 9
+            assert len(res["results"]["data"][0]) == 3
 
     @mock.patch("metobsapi.data_api.query")
-    def test_one_symbol_csv(self, query_func):
+    def test_one_symbol_csv(self, query_func, client):
         r = fake_data("1m", {("aoss", "tower"): ["time", "air_temp"]}, 9)
         query_func.return_value = r
         # row should be the default
-        res = self.app.get("/api/data.csv?symbols=aoss.tower.air_temp&begin=-00:10:00")
+        res = client.get("/api/data.csv?symbols=aoss.tower.air_temp&begin=-00:10:00")
         res = res.data.decode()
         # header, data, newline at end
         lines = res.split("\n")
-        self.assertEqual(len(lines), 5 + 9 + 1)
+        assert len(lines) == 5 + 9 + 1
         # time + 1 channel
-        self.assertEqual(len(lines[5].split(",")), 2)
-        self.assertIn("# code: 200", res)
+        assert len(lines[5].split(",")) == 2
+        assert "# code: 200" in res
 
     @mock.patch("metobsapi.data_api.query")
-    def test_one_symbol_xml(self, query_func):
+    def test_one_symbol_xml(self, query_func, client):
         from xml.dom.minidom import parseString
 
         r = fake_data("1m", {("aoss", "tower"): ["time", "air_temp"]}, 9)
         query_func.return_value = r
         # row should be the default
-        res = self.app.get("/api/data.xml?symbols=aoss.tower.air_temp&begin=-00:10:00")
+        res = client.get("/api/data.xml?symbols=aoss.tower.air_temp&begin=-00:10:00")
         res = parseString(res.data.decode())
         # symbols: time and air_temp
-        self.assertEqual(len(res.childNodes[0].childNodes[0].childNodes), 2)
+        assert len(res.childNodes[0].childNodes[0].childNodes) == 2
         # data rows
-        self.assertEqual(len(res.childNodes[0].childNodes[1].childNodes), 9)
+        assert len(res.childNodes[0].childNodes[1].childNodes) == 9
 
     @mock.patch("metobsapi.data_api.query")
-    def test_three_symbol_csv(self, query_func):
+    def test_three_symbol_csv(self, query_func, client):
         """Test that multiple channels in a CSV file are structured properly."""
         r = fake_data("1m", {("aoss", "tower"): ["time", "air_temp", "rel_hum", "wind_speed"]}, 9)
         query_func.return_value = r
         # row should be the default
-        res = self.app.get(
+        res = client.get(
             "/api/data.csv?symbols=aoss.tower.air_temp:" "aoss.tower.rel_hum:aoss.tower.wind_speed&begin=-00:10:00"
         )
         res = res.data.decode()
         # header, data, newline at end
         lines = res.split("\n")
-        self.assertEqual(len(lines), 5 + 9 + 1)
+        assert len(lines) == 5 + 9 + 1
         # time + 3 channels
-        self.assertEqual(len(lines[5].split(",")), 4)
-        self.assertIn("# code: 200", res)
+        assert len(lines[5].split(",")) == 4
+        assert "# code: 200" in res
 
     @mock.patch("metobsapi.data_api.query")
-    def test_three_symbol_csv_repeat(self, query_func):
+    def test_three_symbol_csv_repeat(self, query_func, client):
         """Test that multiple channels in a CSV file are structured properly."""
         r = fake_data("1m", {("aoss", "tower"): ["time", "air_temp", "rel_hum", "wind_speed"]}, 9)
         query_func.return_value = r
         # row should be the default
-        res = self.app.get(
+        res = client.get(
             "/api/data.csv?symbols=aoss.tower.air_temp:" "aoss.tower.air_temp:aoss.tower.air_temp&begin=-00:10:00"
         )
         res = res.data.decode()
         # header, data, newline at end
         lines = res.split("\n")
         # header, data (one empty line), newline at end
-        self.assertEqual(len(lines), 5 + 1 + 1)
+        assert len(lines) == 5 + 1 + 1
         # time + 1 channel
-        self.assertEqual(len(lines[5].split(",")), 1)
-        self.assertIn("# code: 400", res)
+        assert len(lines[5].split(",")) == 1
+        assert "# code: 400" in res
 
     # @mock.patch('metobsapi.data_api.query')
-    # def test_jsonp_bad_symbol_400(self, query_func):
+    # def test_jsonp_bad_symbol_400(self, query_func, client):
     #     XXX: Not currently possible with flask-json
     #     r = fake_data('1m', {('aoss', 'tower'): ['time', 'air_temp']}, 9)
     #     query_func.return_value = r
     #     # row should be the default
-    #     res = self.app.get('/api/data.json?site=aoss&inst=tower&symbols=bad&begin=-00:10:00&callback=test')
-    #     self.assertEqual(res.status_code, 400)
+    #     res = client.get('/api/data.json?site=aoss&inst=tower&symbols=bad&begin=-00:10:00&callback=test')
+    #     assert res.status_code == 400
     #     res = res.data.decode()
-    #     self.assertEqual(res['code'], 400)
+    #     assert res['code'] == 400