Repository: DOV-Vlaanderen/pydov

Issue:
Make DOV base URL configurable
In order to be able to foresee future problems in pydov regarding changes in the DOV webservices, we need to be able to run pydov (and/or pydov tests) against the DOV development environment.

This would for example have prevented issue #188 to have occurred at the point it did.

At the moment the URLs of the DOV webservices are hardcoded in pydov (to the production environment), it would be better to make these (base) URLs configurable so we can run pydov against the dev environment too.

Since this is functionality for developers only and should not be used by end users, maybe we should not add this to the existing configurable items in pydov/__init__.py, but instead make it overridable with an environment variable or the like.

Discussion:

Patch:
diff --git a/.travis.yml b/.travis.yml
index 9ef1e62..6c899e2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -30,6 +30,10 @@ matrix:
     env: TOXENV=py37-nolxml
   - python: 3.7
     env: TOXENV=py37-lxml
+  - python: 3.7
+    env: TOXENV=update-oefen
+  - python: 3.7
+    env: TOXENV=update-productie
   - python: 3.7
     env: TOXENV=docs
   - env: TOXENV=flake8
diff --git a/appveyor.yml b/appveyor.yml
index d807184..6fdb2df 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,13 +1,13 @@
 environment:
   matrix:
-    - PYTHON_VERSION: "3.5"
-      PYTHON_ARCH: "64"
-      CONDA_PY: "35"
-      CONDA_INSTALL_LOCN: "C:\\Miniconda35-x64"
-    - PYTHON_VERSION: "3.5"
-      PYTHON_ARCH: "64"
-      CONDA_PY: "35"
-      CONDA_INSTALL_LOCN: "C:\\Miniconda35-x64"
+    - PYTHON_VERSION: "2.7"
+      PYTHON_ARCH: "32"
+      CONDA_PY: "27"
+      CONDA_INSTALL_LOCN: "C:\\Miniconda"
+    - PYTHON_VERSION: "2.7"
+      PYTHON_ARCH: "32"
+      CONDA_PY: "27"
+      CONDA_INSTALL_LOCN: "C:\\Miniconda"
       PY_INSTALL: "lxml"
     - PYTHON_VERSION: "3.6"
       PYTHON_ARCH: "64"
@@ -27,15 +27,19 @@ environment:
       CONDA_PY: "37"
       CONDA_INSTALL_LOCN: "C:\\Miniconda36-x64"
       PY_INSTALL: "lxml"
-    - PYTHON_VERSION: "2.7"
-      PYTHON_ARCH: "32"
-      CONDA_PY: "27"
-      CONDA_INSTALL_LOCN: "C:\\Miniconda"
-    - PYTHON_VERSION: "2.7"
-      PYTHON_ARCH: "32"
-      CONDA_PY: "27"
-      CONDA_INSTALL_LOCN: "C:\\Miniconda"
+    - PYTHON_VERSION: "3.7"
+      PYTHON_ARCH: "64"
+      CONDA_PY: "37"
+      CONDA_INSTALL_LOCN: "C:\\Miniconda36-x64"
       PY_INSTALL: "lxml"
+      PYDOV_BASE_URL: "https://oefen.dov.vlaanderen.be/"
+      PYDOV_UPDATE_TESTDATA: "true"
+    - PYTHON_VERSION: "3.7"
+      PYTHON_ARCH: "64"
+      CONDA_PY: "37"
+      CONDA_INSTALL_LOCN: "C:\\Miniconda36-x64"
+      PY_INSTALL: "lxml"
+      PYDOV_UPDATE_TESTDATA: "true"
 install:
     # Use the pre-installed Miniconda for the desired arch
     - cmd: call %CONDA_INSTALL_LOCN%\Scripts\activate.bat
@@ -47,5 +51,9 @@ install:
 
 build: false
 
+before_test:
+    - cmd: set PYTHONPATH=%PYTHONPATH%;%APPVEYOR_BUILD_FOLDER%
+    - ps: if($env:PYDOV_UPDATE_TESTDATA) { & ($env:CONDA_INSTALL_LOCN + "\python.exe") tests\data\update_test_data.py }
+
 test_script:
   - pytest
diff --git a/pydov/search/abstract.py b/pydov/search/abstract.py
index f20ffe9..69d152d 100644
--- a/pydov/search/abstract.py
+++ b/pydov/search/abstract.py
@@ -13,6 +13,7 @@ from owslib.wfs import WebFeatureService
 from pydov.util import owsutil
 from pydov.util.dovutil import (
     get_xsd_schema,
+    build_dov_url,
 )
 from pydov.util.errors import (
     LayerNotFoundError,
@@ -117,8 +118,7 @@ class AbstractSearch(AbstractCommon):
         """
         if AbstractSearch.__wfs is None:
             AbstractSearch.__wfs = WebFeatureService(
-                url="https://www.dov.vlaanderen.be/geoserver/wfs",
-                version="1.1.0")
+                url=build_dov_url('geoserver/wfs'), version="1.1.0")
 
     def _init_namespace(self):
         """Initialise the WFS namespace associated with the layer.
@@ -182,7 +182,7 @@ class AbstractSearch(AbstractCommon):
         layername = self._layer.split(':')[1] if ':' in self._layer else \
             self._layer
         return get_remote_schema(
-            'https://www.dov.vlaanderen.be/geoserver/wfs', layername, '1.1.0')
+            build_dov_url('geoserver/wfs'), layername, '1.1.0')
 
     def _get_namespace(self):
         """Get the WFS namespace of the layer.
diff --git a/pydov/types/grondwaterfilter.py b/pydov/types/grondwaterfilter.py
index 5378743..119e9e0 100644
--- a/pydov/types/grondwaterfilter.py
+++ b/pydov/types/grondwaterfilter.py
@@ -6,13 +6,14 @@ from pydov.types.fields import (
     XsdType,
     WfsField,
 )
+from pydov.util.dovutil import build_dov_url
 from .abstract import (
     AbstractDovType,
     AbstractDovSubType,
 )
 
-_filterDataCodes_xsd = 'https://www.dov.vlaanderen.be/xdov/schema/' \
-                       'latest/xsd/kern/gwmeetnet/FilterDataCodes.xsd'
+_filterDataCodes_xsd = build_dov_url(
+    'xdov/schema/latest/xsd/kern/gwmeetnet/FilterDataCodes.xsd')
 
 
 class Peilmeting(AbstractDovSubType):
@@ -87,7 +88,7 @@ class GrondwaterFilter(AbstractDovType):
         XmlField(name='meetnet_code',
                  source_xpath='/filter/meetnet',
                  definition='Tot welk meetnet behoort deze filter.',
-                 datatype='integer',
+                 datatype='string',
                  xsd_type=XsdType(
                      xsd_schema=_filterDataCodes_xsd,
                      typename='MeetnetEnumType')),
@@ -97,9 +98,9 @@ class GrondwaterFilter(AbstractDovType):
                             '(code).',
                  datatype='string',
                  xsd_type=XsdType(
-                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
-                                'latest/xsd/kern/interpretatie/'
-                                'HydrogeologischeStratigrafieDataCodes.xsd',
+                     xsd_schema=build_dov_url(
+                         'xdov/schema/latest/xsd/kern/interpretatie/'
+                         'HydrogeologischeStratigrafieDataCodes.xsd'),
                      typename='AquiferEnumType')),
         XmlField(name='grondwaterlichaam_code',
                  source_xpath='/filter/ligging/grondwaterlichaam',
diff --git a/pydov/types/interpretaties.py b/pydov/types/interpretaties.py
index 161669b..0ab593f 100644
--- a/pydov/types/interpretaties.py
+++ b/pydov/types/interpretaties.py
@@ -13,6 +13,7 @@ from pydov.types.fields import (
     XmlField,
     XsdType,
 )
+from pydov.util.dovutil import build_dov_url
 
 
 class AbstractCommonInterpretatie(AbstractDovType):
@@ -228,9 +229,9 @@ class FormeleStratigrafieLaag(AbstractDovSubType):
                  definition='eerste eenheid van de laag formele stratigrafie',
                  datatype='string',
                  xsd_type=XsdType(
-                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
-                                'latest/xsd/kern/interpretatie/'
-                                'FormeleStratigrafieDataCodes.xsd',
+                     xsd_schema=build_dov_url(
+                         'xdov/schema/latest/xsd/kern/interpretatie/'
+                         'FormeleStratigrafieDataCodes.xsd'),
                      typename='FormeleStratigrafieLedenEnumType')),
         XmlField(name='relatie_lid1_lid2',
                  source_xpath='/relatie_lid1_lid2',
@@ -238,9 +239,9 @@ class FormeleStratigrafieLaag(AbstractDovSubType):
                             'laag formele stratigrafie',
                  datatype='string',
                  xsd_type=XsdType(
-                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
-                                'latest/xsd/kern/interpretatie/'
-                                'InterpretatieDataCodes.xsd',
+                     xsd_schema=build_dov_url(
+                         'xdov/schema/latest/xsd/kern/interpretatie/'
+                         'InterpretatieDataCodes.xsd'),
                      typename='RelatieLedenEnumType')),
         XmlField(name='lid2',
                  source_xpath='/lid2',
@@ -249,9 +250,9 @@ class FormeleStratigrafieLaag(AbstractDovSubType):
                       'ingevuld',
                  datatype='string',
                  xsd_type=XsdType(
-                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
-                                'latest/xsd/kern/interpretatie/'
-                                'FormeleStratigrafieDataCodes.xsd',
+                     xsd_schema=build_dov_url(
+                         'xdov/schema/latest/xsd/kern/interpretatie/'
+                         'FormeleStratigrafieDataCodes.xsd'),
                      typename='FormeleStratigrafieLedenEnumType'))
     ]
 
@@ -284,9 +285,9 @@ class HydrogeologischeStratigrafieLaag(AbstractDovSubType):
                             'Hydrogeologische stratigrafie zich bevindt.',
                  datatype='string',
                  xsd_type=XsdType(
-                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
-                                'latest/xsd/kern/interpretatie/'
-                                'HydrogeologischeStratigrafieDataCodes.xsd',
+                     xsd_schema=build_dov_url(
+                         'xdov/schema/latest/xsd/kern/interpretatie/'
+                         'HydrogeologischeStratigrafieDataCodes.xsd'),
                      typename='AquiferEnumType'
                  ))
     ]
@@ -334,14 +335,15 @@ class GecodeerdeLithologieLaag(AbstractDovSubType):
     rootpath = './/gecodeerdelithologie/laag'
 
     __gecodeerdHoofdnaamCodesEnumType = XsdType(
-        xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/'
-                   'kern/interpretatie/GecodeerdeLithologieDataCodes.xsd',
+        xsd_schema=build_dov_url('xdov/schema/latest/xsd/kern/interpretatie/'
+                                 'GecodeerdeLithologieDataCodes.xsd'),
         typename='GecodeerdHoofdnaamCodesEnumType'
     )
 
     __gecodeerdBijmengingHoeveelheidEnumType = XsdType(
-        xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/'
-                   'kern/interpretatie/GecodeerdeLithologieDataCodes.xsd',
+        xsd_schema=build_dov_url(
+            'xdov/schema/latest/xsd/kern/interpretatie/'
+            'GecodeerdeLithologieDataCodes.xsd'),
         typename='GecodeerdBijmengingHoeveelheidEnumType'
     )
 
@@ -428,14 +430,16 @@ class GeotechnischeCoderingLaag(AbstractDovSubType):
     rootpath = './/geotechnischecodering/laag'
 
     __geotechnischeCoderingHoofdnaamCodesEnumType = XsdType(
-        xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/'
-                   'kern/interpretatie/GeotechnischeCoderingDataCodes.xsd',
+        xsd_schema=build_dov_url(
+            'xdov/schema/latest/xsd/kern/interpretatie/'
+            'GeotechnischeCoderingDataCodes.xsd'),
         typename='GeotechnischeCoderingHoofdnaamCodesEnumType'
     )
 
     __gtCoderingBijmengingHoeveelheidEnumType = XsdType(
-        xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/latest/xsd/'
-                   'kern/interpretatie/GeotechnischeCoderingDataCodes.xsd',
+        xsd_schema=build_dov_url(
+            'xdov/schema/latest/xsd/kern/interpretatie/'
+            'GeotechnischeCoderingDataCodes.xsd'),
         typename='GeotechnischeCoderingBijmengingHoeveelheidEnumType'
     )
 
@@ -542,9 +546,9 @@ class QuartairStratigrafieLaag(AbstractDovSubType):
                  definition='eerste eenheid van de laag quartairstratigrafie',
                  datatype='string',
                  xsd_type=XsdType(
-                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
-                                'latest/xsd/kern/interpretatie/'
-                                'QuartairStratigrafieDataCodes.xsd',
+                     xsd_schema=build_dov_url(
+                         'xdov/schema/latest/xsd/kern/interpretatie/'
+                         'QuartairStratigrafieDataCodes.xsd'),
                      typename='QuartairStratigrafieLedenEnumType')),
         XmlField(name='relatie_lid1_lid2',
                  source_xpath='/relatie_lid1_lid2',
@@ -552,9 +556,9 @@ class QuartairStratigrafieLaag(AbstractDovSubType):
                             'laag quartairstratigrafie',
                  datatype='string',
                  xsd_type=XsdType(
-                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
-                                'latest/xsd/kern/interpretatie/'
-                                'InterpretatieDataCodes.xsd',
+                     xsd_schema=build_dov_url(
+                         'xdov/schema/latest/xsd/kern/interpretatie/'
+                         'InterpretatieDataCodes.xsd'),
                      typename='RelatieLedenEnumType')),
         XmlField(name='lid2',
                  source_xpath='/lid2',
@@ -563,9 +567,9 @@ class QuartairStratigrafieLaag(AbstractDovSubType):
                       'als voor Lid1 ingevuld',
                  datatype='string',
                  xsd_type=XsdType(
-                     xsd_schema='https://www.dov.vlaanderen.be/xdov/schema/'
-                                'latest/xsd/kern/interpretatie/'
-                                'QuartairStratigrafieDataCodes.xsd',
+                     xsd_schema=build_dov_url(
+                         'xdov/schema/latest/xsd/kern/interpretatie/'
+                         'QuartairStratigrafieDataCodes.xsd'),
                      typename='QuartairStratigrafieLedenEnumType'))
     ]
 
diff --git a/pydov/util/caching.py b/pydov/util/caching.py
index 00a880a..9415c33 100644
--- a/pydov/util/caching.py
+++ b/pydov/util/caching.py
@@ -116,7 +116,8 @@ class AbstractFileCache(AbstractCache):
         self.max_age = max_age
 
         self._re_type_key = re.compile(
-            r'https?://www\.dov\.vlaanderen\.be/data/([^ /]+)/([^.]+)')
+            r'https?://(www|oefen|ontwikkel)\.dov\.vlaanderen\.be/'
+            r'data/([^ /]+)/([^.]+)')
 
         try:
             if not os.path.exists(self.cachedir):
@@ -161,8 +162,8 @@ class AbstractFileCache(AbstractCache):
 
         """
         datatype = self._re_type_key.search(url)
-        if datatype and len(datatype.groups()) > 1:
-            return datatype.group(1), datatype.group(2)
+        if datatype and len(datatype.groups()) > 2:
+            return datatype.group(2), datatype.group(3)
 
     def _get_type_key_from_path(self, path):
         """Parse a filepath and return the datatype and object key.
diff --git a/pydov/util/dovutil.py b/pydov/util/dovutil.py
index 43044ff..b65aeb1 100644
--- a/pydov/util/dovutil.py
+++ b/pydov/util/dovutil.py
@@ -1,11 +1,30 @@
 # -*- coding: utf-8 -*-
 """Module grouping utility functions for DOV XML services."""
+import os
 
 from owslib.etree import etree
 from pydov.util.errors import XmlParseError
 import pydov
 
 
+def build_dov_url(path):
+    """Build the DOV url consisting of the fixed DOV base url, appended with
+    the given path.
+
+    Returns
+    -------
+    str
+        The absolute DOV url.
+
+    """
+    if 'PYDOV_BASE_URL' in os.environ:
+        base_url = os.environ['PYDOV_BASE_URL']
+    else:
+        base_url = 'https://www.dov.vlaanderen.be/'
+
+    return base_url + path.lstrip('/')
+
+
 def get_remote_url(url):
     """Request the URL from the remote service and return its contents.
 
diff --git a/tox.ini b/tox.ini
index d92098d..a299cd7 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = {py27,py35,py36,py37}-{nolxml,lxml}, flake8, docs
+envlist = {py27,py35,py36,py37}-{nolxml,lxml}, update-oefen, update-productie, flake8, docs
 
 [travis]
 python =
@@ -39,6 +39,30 @@ commands =
     pip install -U pip
     py.test --basetemp={envtmpdir} --cov=pydov
 
+[testenv:update-oefen]
+basepython=python3.7
+setenv =
+    PYTHONPATH = {toxinidir}
+    PYDOV_BASE_URL = https://oefen.dov.vlaanderen.be/
+deps =
+    -r{toxinidir}/requirements_dev.txt
+    lxml
+commands =
+    pip install -U pip
+    python3 {toxinidir}/tests/data/update_test_data.py
+    py.test --basetemp={envtmpdir} --cov=pydov
+
+[testenv:update-productie]
+basepython=python3.7
+setenv =
+    PYTHONPATH = {toxinidir}
+deps =
+    -r{toxinidir}/requirements_dev.txt
+    lxml
+commands =
+    pip install -U pip
+    python3 {toxinidir}/tests/data/update_test_data.py
+    py.test --basetemp={envtmpdir} --cov=pydov
 
 ; If you want to make tox run the tests with the same versions, create a
 ; requirements.txt with the pinned versions and uncomment the following lines:


Test patch:
diff --git a/tests/abstract.py b/tests/abstract.py
index e968722..aa6f976 100644
--- a/tests/abstract.py
+++ b/tests/abstract.py
@@ -25,6 +25,7 @@ from owslib.fes import (
 )
 from owslib.etree import etree
 from pydov.types.abstract import AbstractField
+from pydov.util.dovutil import build_dov_url
 from pydov.util.errors import InvalidFieldError
 from pydov.util.location import (
     Within,
@@ -63,8 +64,8 @@ def service_ok(timeout=5):
             ok = False
         return ok
 
-    return check_url('https://www.dov.vlaanderen.be/geoserver', timeout) and\
-        check_url('https://www.dov.vlaanderen.be/geonetwork', timeout)
+    return check_url(build_dov_url('geoserver'), timeout) and\
+        check_url(build_dov_url('geonetwork'), timeout)
 
 
 def clean_xml(xml):
@@ -973,8 +974,7 @@ class AbstractTestTypes(object):
         assert feature.pkey.startswith(self.get_pkey_base())
 
         assert feature.pkey.startswith(
-            'https://www.dov.vlaanderen.be/data/%s/' %
-            feature.typename)
+            build_dov_url('data/{}/'.format(feature.typename)))
 
         assert type(feature.data) is dict
         assert type(feature.subdata) is dict
@@ -1020,8 +1020,7 @@ class AbstractTestTypes(object):
                     assert type(value) is bool or np.isnan(value)
 
                 if field['name'].startswith('pkey') and not pd.isnull(value):
-                    assert value.startswith(
-                        'https://www.dov.vlaanderen.be/data/')
+                    assert value.startswith(build_dov_url('data/'))
                     assert not value.endswith('.xml')
 
     def test_get_df_array_wrongreturnfields(self, wfs_feature):
diff --git a/tests/data/update_test_data.py b/tests/data/update_test_data.py
index af0bfa1..43be636 100644
--- a/tests/data/update_test_data.py
+++ b/tests/data/update_test_data.py
@@ -1,4 +1,6 @@
 """Script to update the testdata based on DOV webservices."""
+import os
+
 import sys
 
 from owslib.etree import etree
@@ -16,8 +18,10 @@ from pydov.types.interpretaties import (
     FormeleStratigrafie,
     InformeleStratigrafie,
     QuartairStratigrafie,
+    InformeleHydrogeologischeStratigrafie,
 )
 from pydov.types.sondering import Sondering
+from pydov.util.dovutil import build_dov_url
 
 
 def get_first_featuremember(wfs_response):
@@ -33,6 +37,7 @@ def get_first_featuremember(wfs_response):
 
 def update_file(filepath, url, process_fn=None):
     sys.stdout.write('Updating {} ...'.format(filepath))
+    filepath = os.path.join(os.path.dirname(__file__), filepath)
     try:
         data = openURL(url).read()
         if type(data) is bytes:
@@ -52,36 +57,38 @@ if __name__ == '__main__':
     # types/boring
 
     update_file('types/boring/boring.xml',
-                'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+                build_dov_url('data/boring/2004-103984.xml'))
 
     update_file('types/boring/wfsgetfeature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=dov-pub:Boringen'
-                '&maxFeatures=1&CQL_Filter=fiche=%27https://www.dov'
-                '.vlaanderen.be/data/boring/2004-103984%27')
+                '&maxFeatures=1&CQL_Filter=fiche=%27' + build_dov_url(
+                    'data/boring/2004-103984%27')))
 
     update_file('types/boring/feature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=dov-pub:Boringen'
-                '&maxFeatures=1&CQL_Filter=fiche=%27https://www.dov'
-                '.vlaanderen.be/data/boring/2004-103984%27',
+                '&maxFeatures=1&CQL_Filter=fiche=%27' + build_dov_url(
+                    'data/boring/2004-103984%27')),
                 get_first_featuremember)
 
     update_file('types/boring/fc_featurecatalogue.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gfc'
-                '&elementSetName=full&id=c0cbd397-520f-4ee1-aca7-d70e271eeed6')
+                '&elementSetName=full&id=c0cbd397-520f-4ee1-aca7'
+                              '-d70e271eeed6'))
 
     update_file('types/boring/md_metadata.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gmd'
-                '&elementSetName=full&id=4e20bf9c-3a5c-42be-b5b6-bef6214d1fa7')
+                '&elementSetName=full&id=4e20bf9c-3a5c-42be-b5b6'
+                              '-bef6214d1fa7'))
 
     update_file('types/boring/wfsdescribefeaturetype.xml',
-                'https://www.dov.vlaanderen.be/geoserver/dov-pub/Boringen'
-                '/ows?service=wfs&version=1.1.0&request=DescribeFeatureType')
+                build_dov_url('geoserver/dov-pub/Boringen'
+                '/ows?service=wfs&version=1.1.0&request=DescribeFeatureType'))
 
     for xsd_schema in Boring.get_xsd_schemas():
         update_file(
@@ -91,36 +98,38 @@ if __name__ == '__main__':
     # types/sondering
 
     update_file('types/sondering/sondering.xml',
-                'https://www.dov.vlaanderen.be/data/sondering/2002-018435.xml')
+                build_dov_url('data/sondering/2002-018435.xml'))
 
     update_file('types/sondering/wfsgetfeature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=dov-pub'
-                ':Sonderingen&maxFeatures=1&CQL_Filter=fiche=%27https://www.'
-                'dov.vlaanderen.be/data/sondering/2002-018435%27')
+                ':Sonderingen&maxFeatures=1&CQL_Filter=fiche=%27' +
+                              build_dov_url('data/sondering/2002-018435%27')))
 
     update_file('types/sondering/feature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=dov-pub'
-                ':Sonderingen&maxFeatures=1&CQL_Filter=fiche=%27https://www.'
-                'dov.vlaanderen.be/data/sondering/2002-018435%27',
+                ':Sonderingen&maxFeatures=1&CQL_Filter=fiche=%27' +
+                              build_dov_url('data/sondering/2002-018435%27')),
                 get_first_featuremember)
 
     update_file('types/sondering/fc_featurecatalogue.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gfc'
-                '&elementSetName=full&id=bd539ba5-5f4d-4c43-9662-51c16caea351')
+                '&elementSetName=full&id=bd539ba5-5f4d-4c43-9662'
+                              '-51c16caea351'))
 
     update_file('types/sondering/md_metadata.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gmd'
-                '&elementSetName=full&id=b397faec-1b64-4854-8000-2375edb3b1a8')
+                '&elementSetName=full&id=b397faec-1b64-4854-8000'
+                              '-2375edb3b1a8'))
 
     update_file('types/sondering/wfsdescribefeaturetype.xml',
-                'https://www.dov.vlaanderen.be/geoserver/dov-pub/Sonderingen'
-                '/ows?service=wfs&version=1.1.0&request=DescribeFeatureType')
+                build_dov_url('geoserver/dov-pub/Sonderingen'
+                '/ows?service=wfs&version=1.1.0&request=DescribeFeatureType'))
 
     for xsd_schema in Sondering.get_xsd_schemas():
         update_file(
@@ -131,335 +140,382 @@ if __name__ == '__main__':
 
     update_file('types/interpretaties/informele_stratigrafie'
                 '/informele_stratigrafie.xml',
-                'https://www.dov.vlaanderen.be/data/interpretatie/1962'
-                '-101692.xml')
+                build_dov_url('data/interpretatie/1962-101692.xml'))
 
     update_file('types/interpretaties/informele_stratigrafie'
                 '/wfsgetfeature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=interpretaties'
                 ':informele_stratigrafie&maxFeatures=1&CQL_Filter'
-                '=Interpretatiefiche=%27https://www.dov.vlaanderen.be/data'
-                '/interpretatie/1962-101692%27')
+                '=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/1962-101692%27'))
 
     update_file('types/interpretaties/informele_stratigrafie/feature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=interpretaties'
                 ':informele_stratigrafie&maxFeatures=1&CQL_Filter'
-                '=Interpretatiefiche=%27https://www.dov.vlaanderen.be/data'
-                '/interpretatie/1962-101692%27',
+                '=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/1962-101692%27'),
                 get_first_featuremember)
 
     update_file(
         'types/interpretaties/informele_stratigrafie/fc_featurecatalogue.xml',
-        'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+        build_dov_url('geonetwork/srv/dut/csw'
         '?Service=CSW&Request=GetRecordById&Version=2.0.2'
         '&outputSchema=http://www.isotc211.org/2005/gfc'
-        '&elementSetName=full&id=b6c651f9-5972-4252-ae10-ad69ad08e78d')
+        '&elementSetName=full&id=b6c651f9-5972-4252-ae10-ad69ad08e78d'))
 
     update_file('types/interpretaties/informele_stratigrafie/md_metadata.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gmd'
-                '&elementSetName=full&id=bd171ea4-2509-478d-a21c-c2728d3a9051')
+                '&elementSetName=full&id=bd171ea4-2509-478d-a21c'
+                              '-c2728d3a9051'))
 
     update_file(
         'types/interpretaties/informele_stratigrafie/wfsdescribefeaturetype'
         '.xml',
-        'https://www.dov.vlaanderen.be/geoserver/interpretaties'
+        build_dov_url('geoserver/interpretaties'
         '/informele_stratigrafie/ows?service=wfs&version=1.1.0&request'
-        '=DescribeFeatureType')
+        '=DescribeFeatureType'))
 
     for xsd_schema in InformeleStratigrafie.get_xsd_schemas():
         update_file(
             'types/interpretaties/informele_stratigrafie/xsd_%s.xml' %
-            xsd_schema.split('/')[-1],
-            xsd_schema)
+            xsd_schema.split('/')[-1], xsd_schema)
 
     # types/interpretaties/formele_stratigrafie
 
     update_file('types/interpretaties/formele_stratigrafie'
                 '/formele_stratigrafie.xml',
-                'https://www.dov.vlaanderen.be/data/interpretatie/2011-'
-                '249333.xml')
+                build_dov_url('data/interpretatie/2011-249333.xml'))
 
     update_file('types/interpretaties/formele_stratigrafie'
                 '/wfsgetfeature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=interpretaties'
                 ':formele_stratigrafie&maxFeatures=1&CQL_Filter'
-                '=Interpretatiefiche=%27https://www.dov.vlaanderen.be/data'
-                '/interpretatie/2011-249333%27')
+                '=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/2011-249333%27'))
 
     update_file('types/interpretaties/formele_stratigrafie/feature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=interpretaties'
                 ':formele_stratigrafie&maxFeatures=1&CQL_Filter'
-                '=Interpretatiefiche=%27https://www.dov.vlaanderen.be/data'
-                '/interpretatie/2011-249333%27',
+                '=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/2011-249333%27'),
                 get_first_featuremember)
 
     update_file(
         'types/interpretaties/formele_stratigrafie/fc_featurecatalogue.xml',
-        'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+        build_dov_url('geonetwork/srv/dut/csw'
         '?Service=CSW&Request=GetRecordById&Version=2.0.2'
         '&outputSchema=http://www.isotc211.org/2005/gfc'
-        '&elementSetName=full&id=68405b5d-51e6-44d0-b634-b580bc2f9eb6')
+        '&elementSetName=full&id=68405b5d-51e6-44d0-b634-b580bc2f9eb6'))
 
     update_file('types/interpretaties/formele_stratigrafie/md_metadata.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gmd'
-                '&elementSetName=full&id=212af8cd-bffd-423c-9d2b-69c544ab3b04')
+                '&elementSetName=full&id=212af8cd-bffd-423c-9d2b'
+                              '-69c544ab3b04'))
 
     update_file(
         'types/interpretaties/formele_stratigrafie/wfsdescribefeaturetype'
         '.xml',
-        'https://www.dov.vlaanderen.be/geoserver/interpretaties'
+        build_dov_url('geoserver/interpretaties'
         '/formele_stratigrafie/ows?service=wfs&version=1.1.0&request'
-        '=DescribeFeatureType')
+        '=DescribeFeatureType'))
 
     for xsd_schema in FormeleStratigrafie.get_xsd_schemas():
         update_file(
             'types/interpretaties/formele_stratigrafie/xsd_%s.xml' %
-            xsd_schema.split('/')[-1],
-            xsd_schema)
+            xsd_schema.split('/')[-1], xsd_schema)
 
     # types/interpretaties/hydrogeologische_stratigrafie
 
     update_file('types/interpretaties/hydrogeologische_stratigrafie'
                 '/hydrogeologische_stratigrafie.xml',
-                'https://www.dov.vlaanderen.be/data/interpretatie/'
-                '2001-186543.xml')
+                build_dov_url('data/interpretatie/2001-186543.xml'))
 
     update_file('types/interpretaties/hydrogeologische_stratigrafie'
                 '/wfsgetfeature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=interpretaties'
                 ':hydrogeologische_stratigrafie&maxFeatures=1&CQL_Filter'
-                '=Interpretatiefiche=%27https://www.dov.vlaanderen.be/data'
-                '/interpretatie/2001-186543%27')
+                '=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/2001-186543%27'))
 
     update_file('types/interpretaties/hydrogeologische_stratigrafie'
                 '/feature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=interpretaties'
                 ':hydrogeologische_stratigrafie&maxFeatures=1&CQL_Filter'
-                '=Interpretatiefiche=%27https://www.dov.vlaanderen.be/data/'
-                'interpretatie/2001-186543%27',
+                '=Interpretatiefiche=%27') + build_dov_url('data/'
+                'interpretatie/2001-186543%27'),
                 get_first_featuremember)
 
     update_file(
         'types/interpretaties/hydrogeologische_stratigrafie/'
         'fc_featurecatalogue.xml',
-        'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+        build_dov_url('geonetwork/srv/dut/csw'
         '?Service=CSW&Request=GetRecordById&Version=2.0.2'
         '&outputSchema=http://www.isotc211.org/2005/gfc'
-        '&elementSetName=full&id=b89e72de-35a9-4bca-8d0b-712d1e881ea6')
+        '&elementSetName=full&id=b89e72de-35a9-4bca-8d0b-712d1e881ea6'))
 
     update_file('types/interpretaties/hydrogeologische_stratigrafie/'
                 'md_metadata.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gmd'
-                '&elementSetName=full&id=25c5d9fa-c2ba-4184-b796-fde790e73d39')
+                '&elementSetName=full&id=25c5d9fa-c2ba-4184-b796'
+                              '-fde790e73d39'))
 
     update_file(
         'types/interpretaties/hydrogeologische_stratigrafie/'
         'wfsdescribefeaturetype.xml',
-        'https://www.dov.vlaanderen.be/geoserver/interpretaties'
+        build_dov_url('geoserver/interpretaties'
         '/hydrogeologische_stratigrafie/ows?service=wfs&version=1.1.0&request'
-        '=DescribeFeatureType')
+        '=DescribeFeatureType'))
 
     for xsd_schema in HydrogeologischeStratigrafie.get_xsd_schemas():
         update_file(
             'types/interpretaties/hydrogeologische_stratigrafie/xsd_%s.xml' %
-            xsd_schema.split('/')[-1],
-            xsd_schema)
+            xsd_schema.split('/')[-1], xsd_schema)
 
     # types/interpretaties/lithologische_beschrijvingen
 
     update_file('types/interpretaties/lithologische_beschrijvingen'
                 '/lithologische_beschrijvingen.xml',
-                'https://www.dov.vlaanderen.be/data/interpretatie/1958'
-                '-003925.xml')
+                build_dov_url('data/interpretatie/1958-003925.xml'))
 
     update_file('types/interpretaties/lithologische_beschrijvingen'
                 '/wfsgetfeature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=interpretaties'
                 ':lithologische_beschrijvingen&maxFeatures=1&CQL_Filter'
-                '=Interpretatiefiche=%27https://www.dov.vlaanderen.be/data'
-                '/interpretatie/1958-003925%27')
+                '=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/1958-003925%27'))
 
     update_file('types/interpretaties/lithologische_beschrijvingen/feature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=interpretaties'
                 ':lithologische_beschrijvingen&maxFeatures=1&CQL_Filter'
-                '=Interpretatiefiche=%27https://www.dov.vlaanderen.be/data'
-                '/interpretatie/1958-003925%27',
+                '=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/1958-003925%27'),
                 get_first_featuremember)
 
     update_file(
         'types/interpretaties/lithologische_beschrijvingen/fc_featurecatalogue.xml',
-        'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+        build_dov_url('geonetwork/srv/dut/csw'
         '?Service=CSW&Request=GetRecordById&Version=2.0.2'
         '&outputSchema=http://www.isotc211.org/2005/gfc'
-        '&elementSetName=full&id=2450d592-29bc-4970-a89f-a7b14bd38dc2')
+        '&elementSetName=full&id=2450d592-29bc-4970-a89f-a7b14bd38dc2'))
 
     update_file('types/interpretaties/lithologische_beschrijvingen/md_metadata.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gmd'
-                '&elementSetName=full&id=45b5610e-9a66-42bd-b920-af099e399f3b')
+                '&elementSetName=full&id=45b5610e-9a66-42bd-b920'
+                              '-af099e399f3b'))
 
     update_file(
         'types/interpretaties/lithologische_beschrijvingen/wfsdescribefeaturetype'
         '.xml',
-        'https://www.dov.vlaanderen.be/geoserver/interpretaties'
+        build_dov_url('geoserver/interpretaties'
         '/lithologische_beschrijvingen/ows?service=wfs&version=1.1.0&request'
-        '=DescribeFeatureType')
+        '=DescribeFeatureType'))
 
     for xsd_schema in LithologischeBeschrijvingen.get_xsd_schemas():
         update_file(
             'types/interpretaties/lithologische_beschrijvingen/xsd_%s.xml' %
-            xsd_schema.split('/')[-1],
-            xsd_schema)
+            xsd_schema.split('/')[-1], xsd_schema)
 
     # types/interpretaties/gecodeerde_lithologie
 
     update_file('types/interpretaties/gecodeerde_lithologie'
                 '/gecodeerde_lithologie.xml',
-                'https://www.dov.vlaanderen.be/data/interpretatie/2001'
-                '-046845.xml')
+                build_dov_url('data/interpretatie/2001-046845.xml'))
 
     update_file('types/interpretaties/gecodeerde_lithologie'
                 '/wfsgetfeature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=interpretaties'
                 ':gecodeerde_lithologie&maxFeatures=1&CQL_Filter'
-                '=Interpretatiefiche=%27https://www.dov.vlaanderen.be/data'
-                '/interpretatie/2001-046845%27')
+                '=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/2001-046845%27'))
 
     update_file('types/interpretaties/gecodeerde_lithologie/feature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=interpretaties'
                 ':gecodeerde_lithologie&maxFeatures=1&CQL_Filter'
-                '=Interpretatiefiche=%27https://www.dov.vlaanderen.be/data'
-                '/interpretatie/2001-046845%27',
+                '=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/2001-046845%27'),
                 get_first_featuremember)
 
     update_file(
         'types/interpretaties/gecodeerde_lithologie/fc_featurecatalogue.xml',
-        'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+        build_dov_url('geonetwork/srv/dut/csw'
         '?Service=CSW&Request=GetRecordById&Version=2.0.2'
         '&outputSchema=http://www.isotc211.org/2005/gfc'
-        '&elementSetName=full&id=0032241d-8920-415e-b1d8-fa0a48154904')
+        '&elementSetName=full&id=0032241d-8920-415e-b1d8-fa0a48154904'))
 
     update_file('types/interpretaties/gecodeerde_lithologie/md_metadata.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gmd'
-                '&elementSetName=full&id=35d630e4-9145-46f9-b7dc-da290a0adc55')
+                '&elementSetName=full&id=35d630e4-9145-46f9-b7dc'
+                              '-da290a0adc55'))
 
     update_file(
         'types/interpretaties/gecodeerde_lithologie/wfsdescribefeaturetype'
         '.xml',
-        'https://www.dov.vlaanderen.be/geoserver/interpretaties'
+        build_dov_url('geoserver/interpretaties'
         '/gecodeerde_lithologie/ows?service=wfs&version=1.1.0&request'
-        '=DescribeFeatureType')
+        '=DescribeFeatureType'))
 
     for xsd_schema in GecodeerdeLithologie.get_xsd_schemas():
         update_file(
             'types/interpretaties/gecodeerde_lithologie/xsd_%s.xml' %
-            xsd_schema.split('/')[-1],
-            xsd_schema)
+            xsd_schema.split('/')[-1], xsd_schema)
 
     # types/interpretaties/geotechnische_codering
 
     update_file('types/interpretaties/geotechnische_codering'
                 '/geotechnische_codering.xml',
-                'https://www.dov.vlaanderen.be/data/interpretatie/2016'
-                '-298511.xml')
+                build_dov_url('data/interpretatie/2016-298511.xml'))
 
     update_file('types/interpretaties/geotechnische_codering'
                 '/wfsgetfeature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=interpretaties'
                 ':geotechnische_coderingen&maxFeatures=1&CQL_Filter'
-                '=Interpretatiefiche=%27https://www.dov.vlaanderen.be/data'
-                '/interpretatie/2016-298511%27')
+                '=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/2016-298511%27'))
 
     update_file('types/interpretaties/geotechnische_codering/feature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=interpretaties'
                 ':geotechnische_coderingen&maxFeatures=1&CQL_Filter'
-                '=Interpretatiefiche=%27https://www.dov.vlaanderen.be/data'
-                '/interpretatie/2016-298511%27',
+                '=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/2016-298511%27'),
                 get_first_featuremember)
 
     update_file(
         'types/interpretaties/geotechnische_codering/fc_featurecatalogue.xml',
-        'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+        build_dov_url('geonetwork/srv/dut/csw'
         '?Service=CSW&Request=GetRecordById&Version=2.0.2'
         '&outputSchema=http://www.isotc211.org/2005/gfc'
-        '&elementSetName=full&id=85404aa6-2d88-46f6-ae5a-575aece71efd')
+        '&elementSetName=full&id=85404aa6-2d88-46f6-ae5a-575aece71efd'))
 
     update_file('types/interpretaties/geotechnische_codering/md_metadata.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gmd'
-                '&elementSetName=full&id=6a3dc5d4-0744-4d9c-85ce-da50913851cc')
+                '&elementSetName=full&id=6a3dc5d4-0744-4d9c-85ce'
+                              '-da50913851cc'))
 
     update_file(
         'types/interpretaties/geotechnische_codering/wfsdescribefeaturetype'
         '.xml',
-        'https://www.dov.vlaanderen.be/geoserver/interpretaties'
+        build_dov_url('geoserver/interpretaties'
         '/geotechnische_coderingen/ows?service=wfs&version=1.1.0&request'
-        '=DescribeFeatureType')
+        '=DescribeFeatureType'))
 
     for xsd_schema in GeotechnischeCodering.get_xsd_schemas():
         update_file(
             'types/interpretaties/geotechnische_codering/xsd_%s.xml' %
-            xsd_schema.split('/')[-1],
-            xsd_schema)
+            xsd_schema.split('/')[-1], xsd_schema)
+
+    # types/interpretaties/informele_hydrogeologische_stratigrafie
+
+    update_file('types/interpretaties/informele_hydrogeologische_stratigrafie'
+                '/informele_hydrogeologische_stratigrafie.xml',
+                build_dov_url('data/interpretatie/2003-297774.xml'))
+
+    update_file('types/interpretaties/informele_hydrogeologische_stratigrafie'
+                '/wfsgetfeature.xml',
+                build_dov_url('geoserver/ows?service=WFS'
+                '&version=1.1.0&request=GetFeature&typeName=interpretaties'
+                ':informele_hydrogeologische_stratigrafie&maxFeatures=1'
+                '&CQL_Filter=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/2003-297774%27'))
+
+    update_file('types/interpretaties/informele_hydrogeologische_stratigrafie'
+                '/feature.xml',
+                build_dov_url('geoserver/ows?service=WFS'
+                '&version=1.1.0&request=GetFeature&typeName=interpretaties'
+                ':informele_hydrogeologische_stratigrafie&maxFeatures=1'
+                '&CQL_Filter=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/2003-297774%27'),
+                get_first_featuremember)
+
+    update_file(
+        'types/interpretaties/informele_hydrogeologische_stratigrafie'
+        '/fc_featurecatalogue.xml',
+        build_dov_url('geonetwork/srv/dut/csw'
+        '?Service=CSW&Request=GetRecordById&Version=2.0.2'
+        '&outputSchema=http://www.isotc211.org/2005/gfc'
+        '&elementSetName=full&id=69f71840-bd29-4b59-9b02-4e36aafaa041'))
+
+    update_file('types/interpretaties/informele_hydrogeologische_stratigrafie'
+                '/md_metadata.xml',
+                build_dov_url('geonetwork/srv/dut/csw'
+                '?Service=CSW&Request=GetRecordById&Version=2.0.2'
+                '&outputSchema=http://www.isotc211.org/2005/gmd'
+                '&elementSetName=full'
+                '&id=ca1d704a-cdee-4968-aa65-9c353863e4b1'))
+
+    update_file(
+        'types/interpretaties/informele_hydrogeologische_stratigrafie/'
+        'wfsdescribefeaturetype.xml',
+        build_dov_url('geoserver/interpretaties'
+        '/informele_hydrogeologische_stratigrafie/'
+        'ows?service=wfs&version=1.1.0&request=DescribeFeatureType'))
+
+    for xsd_schema in InformeleHydrogeologischeStratigrafie.get_xsd_schemas():
+        update_file(
+            'types/interpretaties/informele_hydrogeologische_stratigrafie/'
+            'xsd_%s.xml' % xsd_schema.split('/')[-1], xsd_schema)
 
     # types/grondwaterfilter
 
     update_file('types/grondwaterfilter/grondwaterfilter.xml',
-                'https://www.dov.vlaanderen.be/data/filter/2003-004471.xml')
+                build_dov_url('data/filter/2003-004471.xml'))
 
     update_file('types/grondwaterfilter/wfsgetfeature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName='
                 'gw_meetnetten:meetnetten&maxFeatures=1&'
-                'CQL_Filter=filterfiche=%27https://www.dov'
-                '.vlaanderen.be/data/filter/2003-004471%27')
+                'CQL_Filter=filterfiche=%27' + build_dov_url(
+                    'data/filter/2003-004471%27')))
 
     update_file('types/grondwaterfilter/feature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName='
                 'gw_meetnetten:meetnetten&maxFeatures=1&'
-                'CQL_Filter=filterfiche=%27https://www.dov'
-                '.vlaanderen.be/data/filter/2003-004471%27',
+                'CQL_Filter=filterfiche=%27' + build_dov_url(
+                    'data/filter/2003-004471%27')),
                 get_first_featuremember)
 
     update_file('types/grondwaterfilter/fc_featurecatalogue.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gfc'
-                '&elementSetName=full&id=b142965f-b2aa-429e-86ff-a7cb0e065d48')
+                '&elementSetName=full&id=b142965f-b2aa-429e-86ff'
+                              '-a7cb0e065d48'))
 
     update_file('types/grondwaterfilter/md_metadata.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gmd'
-                '&elementSetName=full&id=6c39d716-aecc-4fbc-bac8-4f05a49a78d5')
+                '&elementSetName=full&id=6c39d716-aecc-4fbc-bac8'
+                              '-4f05a49a78d5'))
 
     update_file('types/grondwaterfilter/wfsdescribefeaturetype.xml',
-                'https://www.dov.vlaanderen.be/geoserver/gw_meetnetten/'
+                build_dov_url('geoserver/gw_meetnetten/'
                 'meetnetten/ows?service=wfs&version=1.1.0&'
-                'request=DescribeFeatureType')
+                'request=DescribeFeatureType'))
 
     for xsd_schema in GrondwaterFilter.get_xsd_schemas():
         update_file(
@@ -469,39 +525,41 @@ if __name__ == '__main__':
     # types/grondwatermonster
 
     update_file('types/grondwatermonster/grondwatermonster.xml',
-                'https://www.dov.vlaanderen.be/data/watermonster/2006-115684.xml')
+                build_dov_url('data/watermonster/2006-115684.xml'))
 
     update_file('types/grondwatermonster/wfsgetfeature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName='
                 'gw_meetnetten:grondwatermonsters&maxFeatures=1&'
-                'CQL_Filter=grondwatermonsterfiche=%27https://www.dov'
-                '.vlaanderen.be/data/watermonster/2006-115684%27')
+                'CQL_Filter=grondwatermonsterfiche=%27' + build_dov_url(
+                    'data/watermonster/2006-115684') + '%27'))
 
     update_file('types/grondwatermonster/feature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName='
                 'gw_meetnetten:grondwatermonsters&maxFeatures=1&'
-                'CQL_Filter=grondwatermonsterfiche=%27https://www.dov'
-                '.vlaanderen.be/data/watermonster/2006-115684%27',
+                'CQL_Filter=grondwatermonsterfiche=%27' + build_dov_url(
+                    'data/watermonster/2006-115684') + '%27'),
                 get_first_featuremember)
 
     update_file('types/grondwatermonster/fc_featurecatalogue.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gfc'
-                '&elementSetName=full&id=639c9612-4bbb-4826-86fd-fec9afd49bf7')
+                '&elementSetName=full&'
+                'id=639c9612-4bbb-4826-86fd-fec9afd49bf7'))
 
     update_file('types/grondwatermonster/md_metadata.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gmd'
-                '&elementSetName=full&id=0b378716-39fb-4151-96c5-2021672f4762')
+                '&elementSetName=full&'
+                'id=0b378716-39fb-4151-96c5-2021672f4762'))
 
     update_file('types/grondwatermonster/wfsdescribefeaturetype.xml',
-                'https://www.dov.vlaanderen.be/geoserver/gw_meetnetten/'
+                build_dov_url('geoserver/gw_meetnetten/'
                 'grondwatermonsters/ows?service=wfs&version=1.1.0&'
-                'request=DescribeFeatureType')
+                'request=DescribeFeatureType'))
 
     for xsd_schema in GrondwaterMonster.get_xsd_schemas():
         update_file(
@@ -512,57 +570,58 @@ if __name__ == '__main__':
     # util/owsutil
 
     update_file('util/owsutil/fc_featurecatalogue_notfound.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gfc'
-                '&elementSetName=full&id=badfc000-0000-0000-0000-badfc00badfc')
+                '&elementSetName=full&id=badfc000-0000-0000-0000'
+                              '-badfc00badfc'))
 
     update_file('util/owsutil/wfscapabilities.xml',
-                'https://www.dov.vlaanderen.be/geoserver/wfs?request'
-                '=getcapabilities&service=wfs&version=1.1.0')
+                build_dov_url('geoserver/wfs?request'
+                '=getcapabilities&service=wfs&version=1.1.0'))
 
     # types/interpretaties/quartaire_stratigrafie
 
     update_file('types/interpretaties/quartaire_stratigrafie'
                 '/quartaire_stratigrafie.xml',
-                'https://www.dov.vlaanderen.be/data/interpretatie/'
-                '1999-057087.xml')
+                build_dov_url('data/interpretatie/1999-057087.xml'))
 
     update_file('types/interpretaties/quartaire_stratigrafie'
                 '/wfsgetfeature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=interpretaties'
                 ':quartaire_stratigrafie&maxFeatures=1&CQL_Filter'
-                '=Interpretatiefiche=%27https://www.dov.vlaanderen.be/data'
-                '/interpretatie/1999-057087%27')
+                '=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/1999-057087%27'))
 
     update_file('types/interpretaties/quartaire_stratigrafie/feature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName=interpretaties'
                 ':quartaire_stratigrafie&maxFeatures=1&CQL_Filter'
-                '=Interpretatiefiche=%27https://www.dov.vlaanderen.be/data'
-                '/interpretatie/1999-057087%27',
+                '=Interpretatiefiche=%27') + build_dov_url('data'
+                '/interpretatie/1999-057087%27'),
                 get_first_featuremember)
 
     update_file(
         'types/interpretaties/quartaire_stratigrafie/fc_featurecatalogue.xml',
-        'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+        build_dov_url('geonetwork/srv/dut/csw'
         '?Service=CSW&Request=GetRecordById&Version=2.0.2'
         '&outputSchema=http://www.isotc211.org/2005/gfc'
-        '&elementSetName=full&id=d40ef884-3278-45db-ad69-2c2a8c3981c3')
+        '&elementSetName=full&id=d40ef884-3278-45db-ad69-2c2a8c3981c3'))
 
     update_file('types/interpretaties/quartaire_stratigrafie/md_metadata.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gmd'
-                '&elementSetName=full&id=8b204ed6-e44c-4567-bbe8-bd427eba082c')
+                '&elementSetName=full&id=8b204ed6-e44c-4567-bbe8'
+                              '-bd427eba082c'))
 
     update_file(
         'types/interpretaties/quartaire_stratigrafie/wfsdescribefeaturetype'
         '.xml',
-        'https://www.dov.vlaanderen.be/geoserver/interpretaties'
+        build_dov_url('geoserver/interpretaties'
         '/quartaire_stratigrafie/ows?service=wfs&version=1.1.0&request'
-        '=DescribeFeatureType')
+        '=DescribeFeatureType'))
 
     for xsd_schema in QuartairStratigrafie.get_xsd_schemas():
         update_file(
@@ -573,43 +632,43 @@ if __name__ == '__main__':
     # types/grondmonster
 
     update_file('types/grondmonster/grondmonster.xml',
-                'https://www.dov.vlaanderen.be/data/grondmonster/'
-                '2017-168758.xml')
+                build_dov_url('data/grondmonster/2017-168758.xml'))
 
     update_file('types/grondmonster/wfsgetfeature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName='
                 'boringen:grondmonsters&maxFeatures=1&CQL_Filter'
-                '=grondmonsterfiche=%27https://www.dov.vlaanderen.be/data'
-                '/grondmonster/2017-168758%27')
+                '=grondmonsterfiche=%27' + build_dov_url('data'
+                '/grondmonster/2017-168758') + '%27'))
 
     update_file('types/grondmonster/feature.xml',
-                'https://www.dov.vlaanderen.be/geoserver/ows?service=WFS'
+                build_dov_url('geoserver/ows?service=WFS'
                 '&version=1.1.0&request=GetFeature&typeName='
                 'boringen:grondmonsters&maxFeatures=1&CQL_Filter'
-                '=grondmonsterfiche=%27https://www.dov.vlaanderen.be/data'
-                '/grondmonster/2017-168758%27',
+                '=grondmonsterfiche=%27' + build_dov_url('data'
+                '/grondmonster/2017-168758') + '%27'),
                 get_first_featuremember)
 
     update_file(
         'types/grondmonster/fc_featurecatalogue.xml',
-        'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+        build_dov_url('geonetwork/srv/dut/csw'
         '?Service=CSW&Request=GetRecordById&Version=2.0.2'
         '&outputSchema=http://www.isotc211.org/2005/gfc'
-        '&elementSetName=full&id=b9338fb5-fc9c-4229-858b-06a5fa3ee49d')
+        '&elementSetName=full&id=b9338fb5-fc9c-4229-858b-06a5fa3ee49d'))
 
     update_file('types/grondmonster/md_metadata.xml',
-                'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+                build_dov_url('geonetwork/srv/dut/csw'
                 '?Service=CSW&Request=GetRecordById&Version=2.0.2'
                 '&outputSchema=http://www.isotc211.org/2005/gmd'
-                '&elementSetName=full&id=6edeab46-2cfc-4aa2-ae03-307d772f34ae')
+                '&elementSetName=full&'
+                'id=6edeab46-2cfc-4aa2-ae03-307d772f34ae'))
 
     update_file(
         'types/grondmonster/wfsdescribefeaturetype'
         '.xml',
-        'https://www.dov.vlaanderen.be/geoserver/boringen'
+        build_dov_url('geoserver/boringen'
         '/grondmonsters/ows?service=wfs&version=1.1.0&request'
-        '=DescribeFeatureType')
+        '=DescribeFeatureType'))
 
     for xsd_schema in Grondmonster.get_xsd_schemas():
         update_file(
diff --git a/tests/test_encoding.py b/tests/test_encoding.py
index 9ecba5c..7fdbd8a 100644
--- a/tests/test_encoding.py
+++ b/tests/test_encoding.py
@@ -12,6 +12,7 @@ import pytest
 from owslib.fes import PropertyIsEqualTo
 from pydov.search.boring import BoringSearch
 from pydov.search.interpretaties import LithologischeBeschrijvingenSearch
+from pydov.util.dovutil import build_dov_url
 from pydov.util.errors import XmlParseWarning
 
 from tests.abstract import (
@@ -63,7 +64,7 @@ class TestEncoding(object):
         boringsearch = BoringSearch()
         query = PropertyIsEqualTo(
             propertyname='pkey_boring',
-            literal='https://www.dov.vlaanderen.be/data/boring/1928-031159')
+            literal=build_dov_url('data/boring/1928-031159'))
 
         df = boringsearch.search(query=query,
                                  return_fields=('pkey_boring', 'uitvoerder'))
@@ -92,7 +93,7 @@ class TestEncoding(object):
         boringsearch = BoringSearch()
         query = PropertyIsEqualTo(
             propertyname='pkey_boring',
-            literal='https://www.dov.vlaanderen.be/data/boring/1928-031159')
+            literal=build_dov_url('data/boring/1928-031159'))
 
         df = boringsearch.search(query=query,
                                  return_fields=('pkey_boring', 'uitvoerder',
@@ -131,7 +132,7 @@ class TestEncoding(object):
         boringsearch = BoringSearch()
         query = PropertyIsEqualTo(
             propertyname='pkey_boring',
-            literal='https://www.dov.vlaanderen.be/data/boring/1928-031159')
+            literal=build_dov_url('data/boring/1928-031159'))
 
         df = boringsearch.search(query=query,
                                  return_fields=('pkey_boring', 'uitvoerder',
@@ -173,7 +174,7 @@ class TestEncoding(object):
         assert not os.path.exists(cached_file)
 
         plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+            build_dov_url('data/boring/1995-056089.xml')),
         assert os.path.exists(cached_file)
 
         with open(cached_file, 'r', encoding='utf-8') as cf:
@@ -184,7 +185,7 @@ class TestEncoding(object):
 
         time.sleep(0.5)
         plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+            build_dov_url('data/boring/1995-056089.xml'))
         # assure we didn't redownload the file:
         assert os.path.getmtime(cached_file) == first_download_time
 
@@ -213,7 +214,7 @@ class TestEncoding(object):
         assert not os.path.exists(cached_file)
 
         gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+            build_dov_url('data/boring/1995-056089.xml')),
         assert os.path.exists(cached_file)
 
         with gzip.open(cached_file, 'rb') as cf:
@@ -224,7 +225,7 @@ class TestEncoding(object):
 
         time.sleep(0.5)
         gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+            build_dov_url('data/boring/1995-056089.xml'))
         # assure we didn't redownload the file:
         assert os.path.getmtime(cached_file) == first_download_time
 
@@ -254,7 +255,7 @@ class TestEncoding(object):
         assert not os.path.exists(cached_file)
 
         ref_data = plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+            build_dov_url('data/boring/1995-056089.xml'))
         assert os.path.exists(cached_file)
 
         with open(cached_file, 'r', encoding='utf-8') as cached:
@@ -288,7 +289,7 @@ class TestEncoding(object):
         assert not os.path.exists(cached_file)
 
         ref_data = gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+            build_dov_url('data/boring/1995-056089.xml'))
         assert os.path.exists(cached_file)
 
         with gzip.open(cached_file, 'rb') as cached:
@@ -322,11 +323,11 @@ class TestEncoding(object):
         assert not os.path.exists(cached_file)
 
         ref_data = plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+            build_dov_url('data/boring/1995-056089.xml'))
         assert os.path.exists(cached_file)
 
         cached_data = plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+            build_dov_url('data/boring/1995-056089.xml'))
 
         assert cached_data == ref_data
 
@@ -356,11 +357,11 @@ class TestEncoding(object):
         assert not os.path.exists(cached_file)
 
         ref_data = gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+            build_dov_url('data/boring/1995-056089.xml'))
         assert os.path.exists(cached_file)
 
         cached_data = gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/1995-056089.xml')
+            build_dov_url('data/boring/1995-056089.xml'))
 
         assert cached_data == ref_data
 
@@ -394,8 +395,7 @@ class TestEncoding(object):
         lithosearch = LithologischeBeschrijvingenSearch()
         query = PropertyIsEqualTo(
             propertyname='pkey_interpretatie',
-            literal='https://www.dov.vlaanderen.be/data/interpretatie/'
-                    '1987-070909')
+            literal=build_dov_url('data/interpretatie/1987-070909'))
 
         try:
             import lxml
diff --git a/tests/test_search.py b/tests/test_search.py
index f6f3212..b7ab6ec 100644
--- a/tests/test_search.py
+++ b/tests/test_search.py
@@ -31,6 +31,9 @@ from pydov.search.interpretaties import GecodeerdeLithologieSearch
 from pydov.search.interpretaties import LithologischeBeschrijvingenSearch
 from pydov.search.sondering import SonderingSearch
 from pydov.search.grondmonster import GrondmonsterSearch
+
+from pydov.util.dovutil import build_dov_url
+
 from pydov.util.errors import (
     InvalidSearchParameterError,
 )
@@ -95,8 +98,7 @@ def wfs(mp_wfs):
 
     """
     return WebFeatureService(
-        url="https://www.dov.vlaanderen.be/geoserver/wfs",
-        version="1.1.0")
+        url=build_dov_url('geoserver/wfs'), version="1.1.0")
 
 
 @pytest.fixture()
diff --git a/tests/test_search_grondwaterfilter.py b/tests/test_search_grondwaterfilter.py
index 9f086a9..a1403af 100644
--- a/tests/test_search_grondwaterfilter.py
+++ b/tests/test_search_grondwaterfilter.py
@@ -4,6 +4,7 @@ import datetime
 from owslib.fes import PropertyIsEqualTo
 from pydov.search.grondwaterfilter import GrondwaterFilterSearch
 from pydov.types.grondwaterfilter import GrondwaterFilter
+from pydov.util.dovutil import build_dov_url
 from tests.abstract import (
     AbstractTestSearch,
 )
@@ -66,8 +67,8 @@ class TestGrondwaterfilterSearch(AbstractTestSearch):
 
         """
         return PropertyIsEqualTo(propertyname='filterfiche',
-                                 literal='https://www.dov.vlaanderen.be/'
-                                         'data/filter/2003-004471')
+                                 literal=build_dov_url(
+                                     'data/filter/2003-004471'))
 
     def get_inexistent_field(self):
         """Get the name of a field that doesn't exist.
@@ -208,4 +209,4 @@ class TestGrondwaterfilterSearch(AbstractTestSearch):
             return_fields=('pkey_filter', 'gw_id', 'filternummer',
                            'meetnet_code'))
 
-        assert df.meetnet_code[0] == 8
+        assert df.meetnet_code[0] == '8'
diff --git a/tests/test_search_nosubtype.py b/tests/test_search_nosubtype.py
index 932f55e..3c9e7db 100644
--- a/tests/test_search_nosubtype.py
+++ b/tests/test_search_nosubtype.py
@@ -2,6 +2,7 @@
 
 from owslib.fes import PropertyIsEqualTo
 from pydov.search.grondwaterfilter import GrondwaterFilterSearch
+from pydov.util.dovutil import build_dov_url
 
 from tests.test_search import (
     mp_wfs,
@@ -61,7 +62,8 @@ class TestSearchNoSubtype(object):
         df = GrondwaterFilterSearch().search(
             query=PropertyIsEqualTo(
                 'pkey_filter',
-                'https://www.dov.vlaanderen.be/data/filter/2007-011302.xml')
+                build_dov_url('data/filter/2007-011302.xml')
+            )
         )
 
         assert len(df.pkey_filter) == 1
diff --git a/tests/test_types_boring.py b/tests/test_types_boring.py
index e7c407c..572b118 100644
--- a/tests/test_types_boring.py
+++ b/tests/test_types_boring.py
@@ -1,6 +1,7 @@
 """Module grouping tests for the pydov.types.boring module."""
 
 from pydov.types.boring import Boring
+from pydov.util.dovutil import build_dov_url
 from tests.abstract import AbstractTestTypes
 
 from tests.test_search_boring import (
@@ -46,7 +47,7 @@ class TestBoring(AbstractTestTypes):
             "https://www.dov.vlaanderen.be/data/boring/"
 
         """
-        return 'https://www.dov.vlaanderen.be/data/boring/'
+        return build_dov_url('data/boring/')
 
     def get_field_names(self):
         """Get the field names for this type as listed in the documentation in
diff --git a/tests/test_types_grondmonster.py b/tests/test_types_grondmonster.py
index 9320c31..d098f04 100644
--- a/tests/test_types_grondmonster.py
+++ b/tests/test_types_grondmonster.py
@@ -1,6 +1,7 @@
 """Module grouping tests for the pydov.types.grondmonster module."""
 
 from pydov.types.grondmonster import Grondmonster
+from pydov.util.dovutil import build_dov_url
 from tests.abstract import AbstractTestTypes
 
 from tests.test_search_grondmonster import (
@@ -46,7 +47,7 @@ class TestGrondmonster(AbstractTestTypes):
             "https://www.dov.vlaanderen.be/data/grondmonster/"
 
         """
-        return 'https://www.dov.vlaanderen.be/data/grondmonster/'
+        return build_dov_url('data/grondmonster/')
 
     def get_field_names(self):
         """Get the field names for this type as listed in the documentation in
diff --git a/tests/test_types_grondwaterfilter.py b/tests/test_types_grondwaterfilter.py
index 1f33d5c..ccccf2f 100644
--- a/tests/test_types_grondwaterfilter.py
+++ b/tests/test_types_grondwaterfilter.py
@@ -1,5 +1,6 @@
 """Module grouping tests for the pydov.types.boring module."""
 from pydov.types.grondwaterfilter import GrondwaterFilter
+from pydov.util.dovutil import build_dov_url
 from tests.abstract import AbstractTestTypes
 
 from tests.test_search_grondwaterfilter import (
@@ -47,7 +48,7 @@ class TestGrondwaterFilter(AbstractTestTypes):
             "https://www.dov.vlaanderen.be/data/boring/"
 
         """
-        return 'https://www.dov.vlaanderen.be/data/filter/'
+        return build_dov_url('data/filter/')
 
     def get_field_names(self):
         """Get the field names for this type as listed in the documentation in
diff --git a/tests/test_types_grondwatermonster.py b/tests/test_types_grondwatermonster.py
index 15e875d..7997c15 100644
--- a/tests/test_types_grondwatermonster.py
+++ b/tests/test_types_grondwatermonster.py
@@ -1,5 +1,6 @@
 """Module grouping tests for the pydov.types.boring module."""
 from pydov.types.grondwatermonster import GrondwaterMonster
+from pydov.util.dovutil import build_dov_url
 from tests.abstract import AbstractTestTypes
 
 from tests.test_search_grondwatermonster import (
@@ -47,7 +48,7 @@ class TestGrondwaterMonster(AbstractTestTypes):
             "https://www.dov.vlaanderen.be/data/boring/"
 
         """
-        return 'https://www.dov.vlaanderen.be/data/watermonster/'
+        return build_dov_url('data/watermonster/')
 
     def get_field_names(self):
         """Get the field names for this type
diff --git a/tests/test_types_itp_formelestratigrafie.py b/tests/test_types_itp_formelestratigrafie.py
index 0cf1605..3f663e6 100644
--- a/tests/test_types_itp_formelestratigrafie.py
+++ b/tests/test_types_itp_formelestratigrafie.py
@@ -1,6 +1,7 @@
 """Module grouping tests for the
 pydov.types.interpretaties.FormeleStratigrafie class."""
 from pydov.types.interpretaties import FormeleStratigrafie
+from pydov.util.dovutil import build_dov_url
 from tests.abstract import AbstractTestTypes
 
 from tests.test_search_itp_formelestratigrafie import (
@@ -48,7 +49,7 @@ class TestFormeleStratigrafie(AbstractTestTypes):
             "https://www.dov.vlaanderen.be/data/interpretatie/"
 
         """
-        return 'https://www.dov.vlaanderen.be/data/interpretatie/'
+        return build_dov_url('data/interpretatie/')
 
     def get_field_names(self):
         """Get the field names for this type as listed in the documentation in
diff --git a/tests/test_types_itp_gecodeerdelithologie.py b/tests/test_types_itp_gecodeerdelithologie.py
index 307e899..71ed0a5 100644
--- a/tests/test_types_itp_gecodeerdelithologie.py
+++ b/tests/test_types_itp_gecodeerdelithologie.py
@@ -1,6 +1,7 @@
 """Module grouping tests for the
 pydov.types.interpretaties.InformeleStratigrafie class."""
 from pydov.types.interpretaties import GecodeerdeLithologie
+from pydov.util.dovutil import build_dov_url
 from tests.abstract import AbstractTestTypes
 
 from tests.test_search_itp_gecodeerdelithologie import (
@@ -48,7 +49,7 @@ class TestGecodeerdeLithologie(AbstractTestTypes):
             "https://www.dov.vlaanderen.be/data/boring/"
 
         """
-        return 'https://www.dov.vlaanderen.be/data/interpretatie/'
+        return build_dov_url('data/interpretatie/')
 
     def get_field_names(self):
         """Get the field names for this type as listed in the documentation in
diff --git a/tests/test_types_itp_geotechnischecodering.py b/tests/test_types_itp_geotechnischecodering.py
index 3dbb2f7..b0060c8 100644
--- a/tests/test_types_itp_geotechnischecodering.py
+++ b/tests/test_types_itp_geotechnischecodering.py
@@ -1,6 +1,7 @@
 """Module grouping tests for the
 pydov.types.interpretaties.GeotechnischeCodering class."""
 from pydov.types.interpretaties import GeotechnischeCodering
+from pydov.util.dovutil import build_dov_url
 from tests.abstract import AbstractTestTypes
 
 from tests.test_search_itp_geotechnischecodering import (
@@ -48,7 +49,7 @@ class TestGeotechnischeCodering(AbstractTestTypes):
             "https://www.dov.vlaanderen.be/data/boring/"
 
         """
-        return 'https://www.dov.vlaanderen.be/data/interpretatie/'
+        return build_dov_url('data/interpretatie/')
 
     def get_field_names(self):
         """Get the field names for this type as listed in the documentation in
diff --git a/tests/test_types_itp_hydrogeologischestratigrafie.py b/tests/test_types_itp_hydrogeologischestratigrafie.py
index 02a09f0..975f925 100644
--- a/tests/test_types_itp_hydrogeologischestratigrafie.py
+++ b/tests/test_types_itp_hydrogeologischestratigrafie.py
@@ -1,6 +1,7 @@
 """Module grouping tests for the
 pydov.types.interpretaties.InformeleStratigrafie class."""
 from pydov.types.interpretaties import HydrogeologischeStratigrafie
+from pydov.util.dovutil import build_dov_url
 from tests.abstract import AbstractTestTypes
 
 from tests.test_search_itp_hydrogeologischestratigrafie import (
@@ -48,7 +49,7 @@ class TestHydrogeologischeStratigrafie(AbstractTestTypes):
             "https://www.dov.vlaanderen.be/data/boring/"
 
         """
-        return 'https://www.dov.vlaanderen.be/data/interpretatie/'
+        return build_dov_url('data/interpretatie/')
 
     def get_field_names(self):
         """Get the field names for this type as listed in the documentation in
diff --git a/tests/test_types_itp_informelehydrogeologischestratigrafie.py b/tests/test_types_itp_informelehydrogeologischestratigrafie.py
index 7315b5c..cd30dcf 100644
--- a/tests/test_types_itp_informelehydrogeologischestratigrafie.py
+++ b/tests/test_types_itp_informelehydrogeologischestratigrafie.py
@@ -4,6 +4,7 @@ from pydov.types.interpretaties import (
     FormeleStratigrafie,
     InformeleHydrogeologischeStratigrafie,
 )
+from pydov.util.dovutil import build_dov_url
 from tests.abstract import AbstractTestTypes
 
 from tests.test_search_itp_formelestratigrafie import (
@@ -51,7 +52,7 @@ class TestInformeleHydrogeologischeFormeleStratigrafie(AbstractTestTypes):
             "https://www.dov.vlaanderen.be/data/interpretatie/"
 
         """
-        return 'https://www.dov.vlaanderen.be/data/interpretatie/'
+        return build_dov_url('data/interpretatie/')
 
     def get_field_names(self):
         """Get the field names for this type as listed in the documentation in
diff --git a/tests/test_types_itp_informelestratigrafie.py b/tests/test_types_itp_informelestratigrafie.py
index 1853b42..b51d93e 100644
--- a/tests/test_types_itp_informelestratigrafie.py
+++ b/tests/test_types_itp_informelestratigrafie.py
@@ -1,6 +1,7 @@
 """Module grouping tests for the
 pydov.types.interpretaties.InformeleStratigrafie class."""
 from pydov.types.interpretaties import InformeleStratigrafie
+from pydov.util.dovutil import build_dov_url
 from tests.abstract import AbstractTestTypes
 
 from tests.test_search_itp_informelestratigrafie import (
@@ -48,7 +49,7 @@ class TestInformeleStratigrafie(AbstractTestTypes):
             "https://www.dov.vlaanderen.be/data/boring/"
 
         """
-        return 'https://www.dov.vlaanderen.be/data/interpretatie/'
+        return build_dov_url('data/interpretatie/')
 
     def get_field_names(self):
         """Get the field names for this type as listed in the documentation in
diff --git a/tests/test_types_itp_lithologischebeschrijvingen.py b/tests/test_types_itp_lithologischebeschrijvingen.py
index 8566edc..cb071e4 100644
--- a/tests/test_types_itp_lithologischebeschrijvingen.py
+++ b/tests/test_types_itp_lithologischebeschrijvingen.py
@@ -1,6 +1,7 @@
 """Module grouping tests for the
 pydov.types.interpretaties.LithologischeBeschrijvingen class."""
 from pydov.types.interpretaties import LithologischeBeschrijvingen
+from pydov.util.dovutil import build_dov_url
 from tests.abstract import AbstractTestTypes
 
 from tests.test_search_itp_lithologischebeschrijvingen import (
@@ -48,7 +49,7 @@ class TestLithologischeBeschrijvingen(AbstractTestTypes):
             "https://www.dov.vlaanderen.be/data/boring/"
 
         """
-        return 'https://www.dov.vlaanderen.be/data/interpretatie/'
+        return build_dov_url('data/interpretatie/')
 
     def get_field_names(self):
         """Get the field names for this type as listed in the documentation in
diff --git a/tests/test_types_itp_quartairstratigrafie.py b/tests/test_types_itp_quartairstratigrafie.py
index 59156e8..9516cc3 100644
--- a/tests/test_types_itp_quartairstratigrafie.py
+++ b/tests/test_types_itp_quartairstratigrafie.py
@@ -1,6 +1,7 @@
 """Module grouping tests for the
 pydov.types.interpretaties.QuartairStratigrafie class."""
 from pydov.types.interpretaties import QuartairStratigrafie
+from pydov.util.dovutil import build_dov_url
 from tests.abstract import AbstractTestTypes
 
 from tests.test_search_itp_quartairstratigrafie import (
@@ -48,7 +49,7 @@ class TestQuartairStratigrafie(AbstractTestTypes):
             "https://www.dov.vlaanderen.be/data/interpretatie/"
 
         """
-        return 'https://www.dov.vlaanderen.be/data/interpretatie/'
+        return build_dov_url('data/interpretatie/')
 
     def get_field_names(self):
         """Get the field names for this type as listed in the documentation in
diff --git a/tests/test_types_pluggable.py b/tests/test_types_pluggable.py
index 34df73b..35df562 100644
--- a/tests/test_types_pluggable.py
+++ b/tests/test_types_pluggable.py
@@ -7,6 +7,7 @@ from pydov.types.abstract import (
 )
 from pydov.types.fields import XmlField
 from pydov.types.grondwaterfilter import GrondwaterFilter
+from pydov.util.dovutil import build_dov_url
 
 from tests.test_search import (
     mp_wfs,
@@ -123,8 +124,7 @@ class TestMyWrongGrondwaterFilter(object):
         with pytest.raises(RuntimeError):
             fs.search(query=PropertyIsEqualTo(
                 propertyname='filterfiche',
-                literal='https://www.dov.vlaanderen.be/data/'
-                        'filter/2003-004471'))
+                literal=build_dov_url('data/filter/2003-004471')))
 
 
 class TestMyGrondwaterFilter(object):
@@ -169,7 +169,7 @@ class TestMyGrondwaterFilter(object):
 
         df = fs.search(query=PropertyIsEqualTo(
             propertyname='filterfiche',
-            literal='https://www.dov.vlaanderen.be/data/filter/2003-004471'))
+            literal=build_dov_url('data/filter/2003-004471')))
 
         assert 'grondwatersysteem' in df
         assert df.iloc[0].grondwatersysteem == 'Centraal Vlaams Systeem'
@@ -223,7 +223,7 @@ class TestMyGrondwaterFilterOpbouw(object):
 
         df = fs.search(query=PropertyIsEqualTo(
             propertyname='filterfiche',
-            literal='https://www.dov.vlaanderen.be/data/filter/2003-004471'))
+            literal=build_dov_url('data/filter/2003-004471')))
 
         assert 'opbouw_van' in df
         assert 'opbouw_tot' in df
diff --git a/tests/test_types_sondering.py b/tests/test_types_sondering.py
index 375e984..2a504c1 100644
--- a/tests/test_types_sondering.py
+++ b/tests/test_types_sondering.py
@@ -1,6 +1,7 @@
 """Module grouping tests for the pydov.types.sondering module."""
 
 from pydov.types.sondering import Sondering
+from pydov.util.dovutil import build_dov_url
 from tests.abstract import AbstractTestTypes
 
 from tests.test_search_sondering import (
@@ -46,7 +47,7 @@ class TestSondering(AbstractTestTypes):
             "https://www.dov.vlaanderen.be/data/sondering/"
 
         """
-        return 'https://www.dov.vlaanderen.be/data/sondering/'
+        return build_dov_url('data/sondering/')
 
     def get_field_names(self):
         """Get the field names for this type as listed in the documentation in
diff --git a/tests/test_util_caching.py b/tests/test_util_caching.py
index b13b748..d9ea17b 100644
--- a/tests/test_util_caching.py
+++ b/tests/test_util_caching.py
@@ -14,6 +14,7 @@ from pydov.util.caching import (
     PlainTextFileCache,
     GzipTextFileCache,
 )
+from pydov.util.dovutil import build_dov_url
 
 
 @pytest.fixture
@@ -136,7 +137,7 @@ class TestPlainTextFileCacheCache(object):
             plaintext_cache.cachedir, 'boring', '2004-103984.xml')
 
         plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert os.path.exists(cached_file)
 
         plaintext_cache.clean()
@@ -171,7 +172,7 @@ class TestPlainTextFileCacheCache(object):
             plaintext_cache.cachedir, 'boring', '2004-103984.xml')
 
         plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert os.path.exists(cached_file)
 
         plaintext_cache.remove()
@@ -203,7 +204,7 @@ class TestPlainTextFileCacheCache(object):
         assert not os.path.exists(cached_file)
 
         plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert os.path.exists(cached_file)
 
     @pytest.mark.parametrize('plaintext_cache', [[]],
@@ -232,14 +233,14 @@ class TestPlainTextFileCacheCache(object):
         assert not os.path.exists(cached_file)
 
         plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert os.path.exists(cached_file)
 
         first_download_time = os.path.getmtime(cached_file)
 
         time.sleep(0.5)
         plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         # assure we didn't redownload the file:
         assert os.path.getmtime(cached_file) == first_download_time
 
@@ -269,14 +270,14 @@ class TestPlainTextFileCacheCache(object):
         assert not os.path.exists(cached_file)
 
         plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert os.path.exists(cached_file)
 
         first_download_time = os.path.getmtime(cached_file)
 
         time.sleep(1.5)
         plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         # assure we did redownload the file, since original is invalid now:
         assert os.path.getmtime(cached_file) > first_download_time
 
@@ -306,7 +307,7 @@ class TestPlainTextFileCacheCache(object):
         assert not os.path.exists(cached_file)
 
         plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert os.path.exists(cached_file)
 
         with open('tests/data/types/boring/boring.xml', 'r',
@@ -344,14 +345,14 @@ class TestPlainTextFileCacheCache(object):
         assert not os.path.exists(cached_file)
 
         plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert os.path.exists(cached_file)
 
         with open('tests/data/types/boring/boring.xml', 'r') as ref:
             ref_data = ref.read().encode('utf-8')
 
         cached_data = plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
 
         assert cached_data == ref_data
 
@@ -381,13 +382,13 @@ class TestPlainTextFileCacheCache(object):
         assert not os.path.exists(cached_file)
 
         ref_data = plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert type(ref_data) is bytes
 
         assert os.path.exists(cached_file)
 
         cached_data = plaintext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert type(cached_data) is bytes
 
 
@@ -418,7 +419,7 @@ class TestGzipTextFileCacheCache(object):
             gziptext_cache.cachedir, 'boring', '2004-103984.xml.gz')
 
         gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert os.path.exists(cached_file)
 
         gziptext_cache.clean()
@@ -453,7 +454,7 @@ class TestGzipTextFileCacheCache(object):
             gziptext_cache.cachedir, 'boring', '2004-103984.xml.gz')
 
         gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert os.path.exists(cached_file)
 
         gziptext_cache.remove()
@@ -485,7 +486,7 @@ class TestGzipTextFileCacheCache(object):
         assert not os.path.exists(cached_file)
 
         gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert os.path.exists(cached_file)
 
     @pytest.mark.parametrize('gziptext_cache', [[]],
@@ -514,14 +515,14 @@ class TestGzipTextFileCacheCache(object):
         assert not os.path.exists(cached_file)
 
         gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert os.path.exists(cached_file)
 
         first_download_time = os.path.getmtime(cached_file)
 
         time.sleep(0.5)
         gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         # assure we didn't redownload the file:
         assert os.path.getmtime(cached_file) == first_download_time
 
@@ -551,14 +552,14 @@ class TestGzipTextFileCacheCache(object):
         assert not os.path.exists(cached_file)
 
         gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert os.path.exists(cached_file)
 
         first_download_time = os.path.getmtime(cached_file)
 
         time.sleep(1.5)
         gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         # assure we did redownload the file, since original is invalid now:
         assert os.path.getmtime(cached_file) > first_download_time
 
@@ -588,7 +589,7 @@ class TestGzipTextFileCacheCache(object):
         assert not os.path.exists(cached_file)
 
         gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert os.path.exists(cached_file)
 
         with open('tests/data/types/boring/boring.xml', 'r',
@@ -626,14 +627,14 @@ class TestGzipTextFileCacheCache(object):
         assert not os.path.exists(cached_file)
 
         gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert os.path.exists(cached_file)
 
         with open('tests/data/types/boring/boring.xml', 'r') as ref:
             ref_data = ref.read().encode('utf-8')
 
         cached_data = gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
 
         assert cached_data == ref_data
 
@@ -663,11 +664,11 @@ class TestGzipTextFileCacheCache(object):
         assert not os.path.exists(cached_file)
 
         ref_data = gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert type(ref_data) is bytes
 
         assert os.path.exists(cached_file)
 
         cached_data = gziptext_cache.get(
-            'https://www.dov.vlaanderen.be/data/boring/2004-103984.xml')
+            build_dov_url('data/boring/2004-103984.xml'))
         assert type(cached_data) is bytes
diff --git a/tests/test_util_dovutil.py b/tests/test_util_dovutil.py
new file mode 100644
index 0000000..31d6b22
--- /dev/null
+++ b/tests/test_util_dovutil.py
@@ -0,0 +1,85 @@
+"""Module grouping tests for the pydov.util.owsutil module."""
+import copy
+import os
+
+import pytest
+
+from pydov.util import dovutil
+
+env_var = "PYDOV_BASE_URL"
+
+
+@pytest.fixture
+def pydov_base_url_environment():
+    """Fixture for setting an environment variable with a different base_url.
+    """
+    old_environ = copy.deepcopy(os.environ)
+    os.environ[env_var] = 'https://dov/'
+
+    yield
+
+    os.environ = old_environ
+
+
+class TestDovutil(object):
+    """Class grouping tests for the pydov.util.dovutil module."""
+
+    def test_get_default_dov_base_url_slash(self):
+        if env_var not in os.environ:
+            assert dovutil.build_dov_url('/geonetwork') == \
+                   'https://www.dov.vlaanderen.be/geonetwork'
+
+    def test_get_default_dov_base_url_multislash(self):
+        if env_var not in os.environ:
+            assert dovutil.build_dov_url('/geonetwork/srv') == \
+                   'https://www.dov.vlaanderen.be/geonetwork/srv'
+
+    def test_get_default_dov_base_url_endslash(self):
+        if env_var not in os.environ:
+            assert dovutil.build_dov_url('/geonetwork/') == \
+                   'https://www.dov.vlaanderen.be/geonetwork/'
+
+    def test_get_default_dov_base_url_noslash(self):
+        if env_var not in os.environ:
+            assert dovutil.build_dov_url('geonetwork') == \
+                   'https://www.dov.vlaanderen.be/geonetwork'
+
+    def test_get_default_dov_base_url_noslash_multi(self):
+        if env_var not in os.environ:
+            assert dovutil.build_dov_url('geonetwork/srv') == \
+                   'https://www.dov.vlaanderen.be/geonetwork/srv'
+
+    def test_get_default_dov_base_url_noslash_end(self):
+        if env_var not in os.environ:
+            assert dovutil.build_dov_url('geonetwork/') == \
+                   'https://www.dov.vlaanderen.be/geonetwork/'
+
+    def test_get_dov_base_url_slash(self, pydov_base_url_environment):
+        assert env_var in os.environ
+        assert dovutil.build_dov_url('/geonetwork') == \
+               'https://dov/geonetwork'
+
+    def test_get_dov_base_url_multislash(self, pydov_base_url_environment):
+        assert env_var in os.environ
+        assert dovutil.build_dov_url('/geonetwork/srv') == \
+               'https://dov/geonetwork/srv'
+
+    def test_get_dov_base_url_endslash(self, pydov_base_url_environment):
+        assert env_var in os.environ
+        assert dovutil.build_dov_url('/geonetwork/') == \
+               'https://dov/geonetwork/'
+
+    def test_get_dov_base_url_noslash(self, pydov_base_url_environment):
+        assert env_var in os.environ
+        assert dovutil.build_dov_url('geonetwork') == \
+               'https://dov/geonetwork'
+
+    def test_get_dov_base_url_noslash_multi(self, pydov_base_url_environment):
+        assert env_var in os.environ
+        assert dovutil.build_dov_url('geonetwork/srv') == \
+               'https://dov/geonetwork/srv'
+
+    def test_get_dov_base_url_noslash_end(self, pydov_base_url_environment):
+        assert env_var in os.environ
+        assert dovutil.build_dov_url('geonetwork/') == \
+               'https://dov/geonetwork/'
diff --git a/tests/test_util_owsutil.py b/tests/test_util_owsutil.py
index bf7da5f..cd3b15c 100644
--- a/tests/test_util_owsutil.py
+++ b/tests/test_util_owsutil.py
@@ -14,6 +14,7 @@ from owslib.fes import (
 from owslib.iso import MD_Metadata
 from owslib.util import nspath_eval
 from pydov.util import owsutil
+from pydov.util.dovutil import build_dov_url
 from pydov.util.errors import (
     MetadataNotFoundError,
     FeatureCatalogueNotFoundError,
@@ -57,7 +58,7 @@ class TestOwsutil(object):
         """
         contentmetadata = wfs.contents['dov-pub:Boringen']
         assert owsutil.get_csw_base_url(contentmetadata) == \
-               'https://www.dov.vlaanderen.be/geonetwork/srv/dut/csw'
+               build_dov_url('geonetwork/srv/dut/csw')
 
     def test_get_csw_base_url_nometadataurls(self, wfs):
         """Test the owsutil.get_csw_base_url method for a layer without
@@ -172,7 +173,7 @@ class TestOwsutil(object):
 
         """
         fc = owsutil.get_remote_featurecatalogue(
-            'https://www.dov.vlaanderen.be/geonetwork/srv/nl/csw',
+            build_dov_url('geonetwork/srv/nl/csw'),
             'c0cbd397-520f-4ee1-aca7-d70e271eeed6')
 
         assert type(fc) is dict
@@ -224,7 +225,7 @@ class TestOwsutil(object):
         """
         with pytest.raises(FeatureCatalogueNotFoundError):
             owsutil.get_remote_featurecatalogue(
-                'https://www.dov.vlaanderen.be/geonetwork/srv/nl/csw',
+                build_dov_url('geonetwork/srv/nl/csw'),
                 'badfc000-0000-0000-0000-badfc00badfc')
 
     def test_get_remote_metadata(self, md_metadata):
diff --git a/tests/test_util_query.py b/tests/test_util_query.py
index 750acc3..b14d9c6 100644
--- a/tests/test_util_query.py
+++ b/tests/test_util_query.py
@@ -3,6 +3,7 @@ import pandas as pd
 import numpy as np
 import pytest
 
+from pydov.util.dovutil import build_dov_url
 from pydov.util.query import (
     PropertyInList,
     Join,
@@ -146,9 +147,9 @@ class TestJoin(object):
         Test whether the generated query is correct.
 
         """
-        l = ['https://www.dov.vlaanderen.be/data/boring/1986-068853',
-             'https://www.dov.vlaanderen.be/data/boring/1986-068843',
-             'https://www.dov.vlaanderen.be/data/boring/1980-068861']
+        l = [build_dov_url('data/boring/1986-068853'),
+             build_dov_url('data/boring/1986-068843'),
+             build_dov_url('data/boring/1980-068861')]
 
         df = pd.DataFrame({
             'pkey_boring': pd.Series(l),
@@ -182,12 +183,12 @@ class TestJoin(object):
         duplicate entry twice.
 
         """
-        l = ['https://www.dov.vlaanderen.be/data/boring/1986-068853',
-             'https://www.dov.vlaanderen.be/data/boring/1986-068853',
-             'https://www.dov.vlaanderen.be/data/boring/1980-068861']
+        l = [build_dov_url('data/boring/1986-068853'),
+             build_dov_url('data/boring/1986-068853'),
+             build_dov_url('data/boring/1980-068861')]
 
-        l_output = ['https://www.dov.vlaanderen.be/data/boring/1986-068853',
-                    'https://www.dov.vlaanderen.be/data/boring/1980-068861']
+        l_output = [build_dov_url('data/boring/1986-068853'),
+                    build_dov_url('data/boring/1980-068861')]
 
         df = pd.DataFrame({
             'pkey_boring': pd.Series(l),
@@ -221,9 +222,9 @@ class TestJoin(object):
 
         """
         with pytest.raises(ValueError):
-            l = ['https://www.dov.vlaanderen.be/data/boring/1986-068853',
-                 'https://www.dov.vlaanderen.be/data/boring/1986-068843',
-                 'https://www.dov.vlaanderen.be/data/boring/1980-068861']
+            l = [build_dov_url('data/boring/1986-068853'),
+                 build_dov_url('data/boring/1986-068843'),
+                 build_dov_url('data/boring/1980-068861')]
 
             df = pd.DataFrame({
                 'pkey_boring': pd.Series(l),
@@ -239,7 +240,7 @@ class TestJoin(object):
         single PropertyIsEqualTo.
 
         """
-        l = ['https://www.dov.vlaanderen.be/data/boring/1986-068853']
+        l = [build_dov_url('data/boring/1986-068853')]
 
         df = pd.DataFrame({
             'pkey_boring': pd.Series(l),
@@ -268,9 +269,9 @@ class TestJoin(object):
         single PropertyIsEqualTo.
 
         """
-        l = ['https://www.dov.vlaanderen.be/data/boring/1986-068853',
-             'https://www.dov.vlaanderen.be/data/boring/1986-068853']
-        l_output = ['https://www.dov.vlaanderen.be/data/boring/1986-068853']
+        l = [build_dov_url('data/boring/1986-068853'),
+             build_dov_url('data/boring/1986-068853')]
+        l_output = [build_dov_url('data/boring/1986-068853')]
 
         df = pd.DataFrame({
             'pkey_boring': pd.Series(l),
@@ -311,9 +312,9 @@ class TestJoin(object):
         Test whether the generated query is correct.
 
         """
-        l = ['https://www.dov.vlaanderen.be/data/boring/1986-068853',
-             'https://www.dov.vlaanderen.be/data/boring/1986-068843',
-             'https://www.dov.vlaanderen.be/data/boring/1980-068861']
+        l = [build_dov_url('data/boring/1986-068853'),
+             build_dov_url('data/boring/1986-068843'),
+             build_dov_url('data/boring/1980-068861')]
 
         df = pd.DataFrame({
             'pkey_boring': pd.Series(l),
@@ -346,9 +347,9 @@ class TestJoin(object):
         Test whether the generated query is correct.
 
         """
-        l = ['https://www.dov.vlaanderen.be/data/boring/1986-068853',
-             'https://www.dov.vlaanderen.be/data/boring/1986-068843',
-             'https://www.dov.vlaanderen.be/data/boring/1980-068861']
+        l = [build_dov_url('data/boring/1986-068853'),
+             build_dov_url('data/boring/1986-068843'),
+             build_dov_url('data/boring/1980-068861')]
 
         df = pd.DataFrame({
             'boringfiche': pd.Series(l),