Run tests in parallel with `pytest-xdist`
@jvavrek can we change the tests to use xdist and run in parallel?

_Originally posted by @jccurtis in https://github.com/lbl-anp/becquerel/issues/333#issuecomment-1087928469_

I was unable to get `pytest-xdist` to work properly—it seems to cause race conditions when used in conjunction with `pytest-cov`. Perhaps we can revisit this in the future. See the original post for further discussion on the original attempt.
diff --git a/requirements-dev.txt b/requirements-dev.txt
index fa475d2..348d9e6 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -3,8 +3,9 @@ bump2version
 coverage
 flake8>=4.0.1
 pre-commit
-pytest<7.0.0
+pytest
 pytest-black
 pytest-cov
 pytest-rerunfailures
 pytest-xdist
+requests
diff --git a/tests/df_cache_test.py b/tests/df_cache_test.py
index 1396e7a..1920a63 100644
--- a/tests/df_cache_test.py
+++ b/tests/df_cache_test.py
@@ -61,6 +61,7 @@ class TestCacheExceptions:
         """Test ExampleCache.check_path() exception for a bad path."""
         d = ExampleCache()
         d.path = "/bad/path"
+        d.filename = "/bad/path/filename.csv"
         with pytest.raises(CacheError):
             d.check_path()
         with pytest.raises(CacheError):
diff --git a/tests/materials_test.py b/tests/materials_test.py
index 6fe3a4f..4baa6ff 100644
--- a/tests/materials_test.py
+++ b/tests/materials_test.py
@@ -12,9 +12,11 @@ from becquerel.tools.materials_nist import convert_composition
 import becquerel.tools.materials as materials
 import becquerel.tools.materials_compendium as materials_compendium
 import pytest
+from utils import xcom_is_up
 
 
 @pytest.mark.webtest
+@pytest.mark.skipif(not xcom_is_up(), reason="XCOM is down.")
 class TestConvertComposition:
     """Test convert_composition."""
 
@@ -51,6 +53,7 @@ class TestConvertComposition:
 
 
 @pytest.mark.webtest
+@pytest.mark.skipif(not xcom_is_up(), reason="XCOM is down.")
 def test_materials():
     """Test fetch_materials."""
     fetch_materials()
@@ -58,6 +61,7 @@ def test_materials():
 
 
 @pytest.mark.webtest
+@pytest.mark.skipif(not xcom_is_up(), reason="XCOM is down.")
 def test_materials_force():
     """Test fetch_materials with force=True."""
     assert os.path.exists(materials.FILENAME)
@@ -87,6 +91,7 @@ def test_materials_dummy_csv():
 
 
 @pytest.mark.webtest
+@pytest.mark.skipif(not xcom_is_up(), reason="XCOM is down.")
 def test_materials_dummy_compendium_pre2022():
     """Test fetch_materials with a dummy Compendium JSON file.
 
@@ -132,6 +137,7 @@ def test_materials_dummy_compendium_pre2022():
 
 
 @pytest.mark.webtest
+@pytest.mark.skipif(not xcom_is_up(), reason="XCOM is down.")
 def test_materials_dummy_compendium_2022():
     """Test fetch_materials with a dummy Compendium JSON file.
 
@@ -186,6 +192,7 @@ def test_materials_dummy_compendium_2022():
 
 
 @pytest.mark.webtest
+@pytest.mark.skipif(not xcom_is_up(), reason="XCOM is down.")
 def test_materials_dummy_compendium_error():
     """Test fetch_materials with a dummy Compendium JSON file.
 
@@ -205,6 +212,7 @@ def test_materials_dummy_compendium_error():
 
 
 @pytest.mark.webtest
+@pytest.mark.skipif(not xcom_is_up(), reason="XCOM is down.")
 def test_materials_no_compendium():
     """Test fetch_materials with no Compendium JSON file."""
     # point to a dummy JSON file that does not exist
diff --git a/tests/nndc_test.py b/tests/nndc_test.py
index 2f66d2d..73e818d 100644
--- a/tests/nndc_test.py
+++ b/tests/nndc_test.py
@@ -3,6 +3,7 @@
 import numpy as np
 import pandas as pd
 from becquerel.tools import nndc
+from utils import nndc_is_up
 import pytest
 
 
@@ -137,7 +138,8 @@ class TestParseFloatUncertainty:
             nndc._parse_float_uncertainty("7", "@")
 
 
-@pytest.mark.flaky(reruns=3)
+@pytest.mark.skipif(not nndc_is_up(), reason="NNDC is down.")
+@pytest.mark.flaky(reruns=10)
 class NNDCQueryTests:
     """Tests common to NNDCQuery-derived classes."""
 
@@ -559,7 +561,8 @@ class NNDCQueryTests:
 
 
 @pytest.mark.webtest
-@pytest.mark.flaky(reruns=3)
+@pytest.mark.skipif(not nndc_is_up(), reason="NNDC is down.")
+@pytest.mark.flaky(reruns=10)
 class TestNuclearWalletCard(NNDCQueryTests):
     """Test NNDC nuclear_wallet_card query."""
 
@@ -703,7 +706,8 @@ class TestNuclearWalletCard(NNDCQueryTests):
 
 
 @pytest.mark.webtest
-@pytest.mark.flaky(reruns=3)
+@pytest.mark.skipif(not nndc_is_up(), reason="NNDC is down.")
+@pytest.mark.flaky(reruns=10)
 class TestDecayRadiationQuery(NNDCQueryTests):
     """Test NNDC decay_radiation."""
 
diff --git a/tests/utils.py b/tests/utils.py
index 94b6fa7..754b505 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -1,5 +1,30 @@
 """Helpers for testing."""
 from pathlib import Path
+import requests
 
 TESTS_PATH = Path(__file__).parent.absolute()
 REPO_PATH = TESTS_PATH.parent
+
+
+def database_is_up(url):
+    """Check whether an online database can be reached.
+
+    Parameters
+    ----------
+    url : str
+        URL to query.
+    """
+    try:
+        return requests.post(url).status_code == requests.codes.ok
+    except requests.exceptions.ConnectionError:
+        return False
+
+
+def nndc_is_up():
+    """Check whether the NNDC databases can be reached."""
+    return database_is_up("https://www.nndc.bnl.gov/nudat3/indx_sigma.jsp")
+
+
+def xcom_is_up():
+    """Check whether the NIST XCOM databases can be reached."""
+    return database_is_up("https://physics.nist.gov/PhysRefData/Xcom/html/xcom1.html")
diff --git a/tests/wallet_cache_test.py b/tests/wallet_cache_test.py
index ff353fe..e746f83 100644
--- a/tests/wallet_cache_test.py
+++ b/tests/wallet_cache_test.py
@@ -4,6 +4,7 @@ import numpy as np
 import uncertainties
 import becquerel.tools.wallet_cache as wallet_cache
 import pytest
+from utils import nndc_is_up
 
 
 @pytest.mark.parametrize(
@@ -49,6 +50,7 @@ def test_format_ufloat(arg, result):
 
 @pytest.mark.webtest
 @pytest.mark.flaky(reruns=3)
+@pytest.mark.skipif(not nndc_is_up(), reason="NNDC is down.")
 class TestWalletCardCache:
     """Test functionality of wallet_cache."""
 
diff --git a/tests/xcom_test.py b/tests/xcom_test.py
index 51fb4fc..99d8159 100644
--- a/tests/xcom_test.py
+++ b/tests/xcom_test.py
@@ -2,6 +2,7 @@
 
 import numpy as np
 from becquerel.tools import xcom
+from utils import xcom_is_up
 import pytest
 
 XCOM_URL_ORIG = xcom._URL
@@ -122,6 +123,7 @@ GE_GRID_ENERGIES_PLUS_3 = [
 
 
 @pytest.mark.webtest
+@pytest.mark.skipif(not xcom_is_up(), reason="XCOM is down.")
 class TestFetchXCOMData:
     """Test fetch_xcom_data function."""
 
['tests/df_cache_test.py', 'tests/materials_test.py', 'tests/xcom_test.py', 'tests/wallet_cache_test.py', 'tests/utils.py', 'tests/nndc_test.py', 'requirements-dev.txt']