diff --git a/formats/gml_format.py b/formats/gml_format.py
index c74e401c580e933ed45c4e10cf33a0c22666fde7..b0dc4f9b32b096ca196bda5a82ba96d7bec937e5 100644
--- a/formats/gml_format.py
+++ b/formats/gml_format.py
@@ -1,6 +1,4 @@
 import geopandas
-from pyogrio.errors import DataSourceError
-from shapely.errors import GEOSException
 
 
 def is_valid(resource, file):
@@ -10,12 +8,6 @@ def is_valid(resource, file):
         try:
             geopandas.read_file(f)
             return True
-        except DataSourceError as e:
-            resource["error"] = str(e)
-            return False
-        except GEOSException as e:
-            resource["error"] = str(e)
-            return False
         except Exception as e:
             resource["error"] = str(e)
             return False
diff --git a/formats/ods_format.py b/formats/ods_format.py
new file mode 100644
index 0000000000000000000000000000000000000000..0ff033b3bded0051c0f69c65ff34028e5e7f4374
--- /dev/null
+++ b/formats/ods_format.py
@@ -0,0 +1,27 @@
+import zipfile
+
+
+def is_valid(resource, file):
+    """Check if the content is a ODS file."""
+
+    if not zipfile.is_zipfile(file.name):
+        resource["error"] = "Not a ZIP file."
+        return False
+
+    with zipfile.ZipFile(file.name, "r") as zip_ref:
+        zip_contents = zip_ref.namelist()
+
+        required_files = ["mimetype", "content.xml", "meta.xml", "styles.xml"]
+
+        if not all(file in zip_contents for file in required_files):
+            resource["error"] = "That does not look like an ODS file."
+            return False
+
+        with zip_ref.open("mimetype") as mimetype_file:
+            mimetype_content = mimetype_file.read().decode("utf-8").strip()
+
+        if mimetype_content != "application/vnd.oasis.opendocument.spreadsheet":
+            resource["error"] = f"Incorrect MIME type: {mimetype_content}"
+            return False
+
+        return True
diff --git a/formats/pdf_format.py b/formats/pdf_format.py
index 4a7ee6944ab8a44061219da9a6e5cd40bf7a3717..2c7e9339bfd9c65ff63357df3ec256c752f0398e 100644
--- a/formats/pdf_format.py
+++ b/formats/pdf_format.py
@@ -1,5 +1,4 @@
 from pypdf import PdfReader
-from pypdf.errors import PyPdfError
 
 
 def is_valid(resource, file):
@@ -9,5 +8,6 @@ def is_valid(resource, file):
         try:
             PdfReader(f)
             return True
-        except PyPdfError:
+        except Exception as e:
+            resource["error"] = str(e)
             return False
diff --git a/formats/png_format.py b/formats/png_format.py
index ec3a7344edea208acc28303723163a663f7ae951..c7a9efb9890e01df4494e43ebd4262bac54fa3b9 100644
--- a/formats/png_format.py
+++ b/formats/png_format.py
@@ -1,4 +1,4 @@
-from PIL import Image, UnidentifiedImageError
+from PIL import Image
 
 
 def is_valid(resource, file):
@@ -7,5 +7,6 @@ def is_valid(resource, file):
     try:
         with Image.open(file.name, formats=["PNG"]):
             return True
-    except UnidentifiedImageError:
+    except Exception as e:
+        resource["error"] = str(e)
         return False
diff --git a/formats/rdf_format.py b/formats/rdf_format.py
new file mode 100644
index 0000000000000000000000000000000000000000..27de8ee1969df75ac51f08b1b425a0c7a9d82b1f
--- /dev/null
+++ b/formats/rdf_format.py
@@ -0,0 +1,19 @@
+from rdflib import Graph
+
+
+def is_valid(resource, file):
+    """Check if file is a valid RDF document."""
+
+    try:
+        graph = Graph()
+        graph.parse(file.name)
+
+        # even an empty RDF document contains two statements
+        if len(graph) > 2:
+            return True
+        else:
+            resource["error"] = "RDF document does not contain any statements."
+            return False
+    except Exception as e:
+        resource["error"] = str(e)
+        return False
diff --git a/formats/shp_format.py b/formats/shp_format.py
index de42333685da6a31c2bbedfd2336d3f61dea69c1..a13329913d7dc028e54d072233520b2b37cde31a 100644
--- a/formats/shp_format.py
+++ b/formats/shp_format.py
@@ -1,6 +1,4 @@
 import geopandas
-from pyogrio.errors import DataSourceError
-from shapely.errors import GEOSException
 import zipfile
 
 
@@ -24,10 +22,7 @@ def is_valid(resource, file):
         with open(file.name, "rb") as f:
             try:
                 geopandas.read_file(f)
-            except DataSourceError as e:
-                resource["error"] = str(e)
-                return False
-            except GEOSException as e:
+            except Exception as e:
                 resource["error"] = str(e)
                 return False
         return True
@@ -37,10 +32,7 @@ def is_valid(resource, file):
                 with z.open(shp) as f:
                     try:
                         geopandas.read_file(f"zip://{file.name}!{shp}")
-                    except DataSourceError as e:
-                        resource["error"] = str(e)
-                        return False
-                    except GEOSException as e:
+                    except Exception as e:
                         resource["error"] = str(e)
                         return False
         return True
diff --git a/formats/wfs_srvc_format.py b/formats/wfs_srvc_format.py
index bdf788ef03c4c357666dae0d3a54f2d41af60518..9ded4c2de06fece7f5e124ce7abcb4792c206adf 100644
--- a/formats/wfs_srvc_format.py
+++ b/formats/wfs_srvc_format.py
@@ -12,21 +12,26 @@ def _load_into_file(url):
         return temp_file
 
 
-def _is_capabilites_response(file):
+def _is_capabilites_response(resource, file):
     with open(file.name, "rb") as f:
         try:
             xml = ET.parse(f).getroot()
 
-            return (
+            if (
                 xml.tag == "{http://www.opengis.net/wfs/2.0}WFS_Capabilities"
                 or xml.tag == "{http://www.opengis.net/wfs}WFS_Capabilities"
-            )
-        except ET.ParseError:
+            ):
+                return True
+            else:
+                resource["error"] = "Root element is not WFS_Capabilities"
+                return False
+        except Exception as e:
+            resource["error"] = str(e)
             return False
 
 
 def is_valid(resource, file):
-    if _is_capabilites_response(file):
+    if _is_capabilites_response(resource, file):
         return True
 
     # The response is not a capabilites XML files. That is allowed.
@@ -38,7 +43,12 @@ def is_valid(resource, file):
             url = url + "?"
 
         url = url + "service=WFS&request=GetCapabilities"
-        return _is_capabilites_response(_load_into_file(url))
+
+        try:
+            return _is_capabilites_response(resource, _load_into_file(url))
+        except Exception as e:
+            resource["error"] = str(e)
+            return False
     else:
         # The URL already contains a getCapabilites request but the result was not a correct answer.
         return False
diff --git a/formats/wms_srvc_format.py b/formats/wms_srvc_format.py
index 7221d623ea97579e6fb2fb79660eea9a4a9d93c3..62634521d7dee2976655a802b728f119b779f3a1 100644
--- a/formats/wms_srvc_format.py
+++ b/formats/wms_srvc_format.py
@@ -12,18 +12,25 @@ def _load_into_file(url):
         return temp_file
 
 
-def _is_capabilites_response(file):
+def _is_capabilites_response(resource, file):
     with open(file.name, "rb") as f:
         try:
             xml = ET.parse(f).getroot()
 
-            return xml.tag == "{http://www.opengis.net/wms}WMS_Capabilities"
-        except ET.ParseError:
+            if xml.tag == "{http://www.opengis.net/wms}WMS_Capabilities":
+                return True
+            else:
+                resource["error"] = (
+                    "Root element is not {http://www.opengis.net/wmts/1.0}WMS_Capabilities"
+                )
+                return False
+        except Exception as e:
+            resource["error"] = str(e)
             return False
 
 
 def is_valid(resource, file):
-    if _is_capabilites_response(file):
+    if _is_capabilites_response(resource, file):
         return True
 
     # The response is not a capabilites XML files. That is allowed.
@@ -35,7 +42,12 @@ def is_valid(resource, file):
             url = url + "?"
 
         url = url + "service=WMS&request=GetCapabilities"
-        return _is_capabilites_response(_load_into_file(url))
+        try:
+            return _is_capabilites_response(resource, _load_into_file(url))
+        except Exception as e:
+            resource["error"] = str(e)
+            return False
+
     else:
         # The URL already contains a getCapabilites request but the result was not a correct answer.
         return False
diff --git a/formats/wmts_srvc_format.py b/formats/wmts_srvc_format.py
new file mode 100644
index 0000000000000000000000000000000000000000..a27c0936c0fe7c1251eafbc367cbac0ea3d0a2aa
--- /dev/null
+++ b/formats/wmts_srvc_format.py
@@ -0,0 +1,53 @@
+import xml.etree.ElementTree as ET
+import requests
+import tempfile
+
+
+def _load_into_file(url):
+    response = requests.get(url)
+    response.raise_for_status()
+
+    with tempfile.NamedTemporaryFile(delete=False) as temp_file:
+        temp_file.write(response.content)
+        return temp_file
+
+
+def _is_capabilites_response(resource, file):
+    with open(file.name, "rb") as f:
+        try:
+            xml = ET.parse(f).getroot()
+
+            if xml.tag == "{http://www.opengis.net/wmts/1.0}Capabilities":
+                return True
+            else:
+                resource["error"] = (
+                    "Root element is not {http://www.opengis.net/wmts/1.0}WMS_Capabilities"
+                )
+                return False
+        except Exception as e:
+            resource["error"] = str(e)
+            return False
+
+
+def is_valid(resource, file):
+    if _is_capabilites_response(resource, file):
+        return True
+
+    # The response is not a capabilites XML files. That is allowed.
+    # Let's add the request parameters to the URL and try again.
+
+    url = resource["url"]
+    if "request=" not in url.lower():
+        if not url.endswith("?"):
+            url = url + "?"
+
+        url = url + "service=WMTS&request=GetCapabilities"
+        try:
+            return _is_capabilites_response(resource, _load_into_file(url))
+        except Exception as e:
+            resource["error"] = str(e)
+            return False
+
+    else:
+        # The URL already contains a getCapabilites request but the result was not a correct answer.
+        return False
diff --git a/formats/zip_format.py b/formats/zip_format.py
new file mode 100644
index 0000000000000000000000000000000000000000..8de8b6a8fd3b61e9b7360ec2f6cb088512af44a0
--- /dev/null
+++ b/formats/zip_format.py
@@ -0,0 +1,11 @@
+import zipfile
+
+
+def is_valid(resource, file):
+    """Check if the file is a ZIP file."""
+
+    if not zipfile.is_zipfile(file.name):
+        resource["error"] = "Not a ZIP file."
+        return False
+
+    return True
diff --git a/tests/data/WMTSCapabilities.xml b/tests/data/WMTSCapabilities.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a9d19dfdc9692770e5cd0430ae73670e251e631f
--- /dev/null
+++ b/tests/data/WMTSCapabilities.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0"?>
+<Capabilities xmlns="http://www.opengis.net/wmts/1.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd" version="1.0.0">
+  <ows:ServiceIdentification>
+    <ows:Title>WMTS_SH_ALKIS</ows:Title>
+    <ows:Abstract>Flächendeckende Beschreibung der Angaben zu den Layern "Flurstücke", "Gebäude" sowie zu den Gruppierungen "Tatsächliche Nutzung" und "Gesetzliche Festlegungen" gemäß der entsprechenden Objektbereiche im ALKIS-Objektartenkatalog. Die Gruppierung "Weiteres" ist optional und enthält die Objektbereiche "Bauwerke und Einrichtungen" sowie "Relief". Alle ALKIS-Objekte des Grunddatenbestandes (ausser Grenzpunkte und Netzpunkte) sind Pflichtinhalte. Alle weiteren ALKIS-Objekte können optional geführt werden. Die Präsentation der ALKIS-Daten erfolgt grundsätzlich nach dem ALKIS-Signaturenkatalog für AdV-Standardausgaben. Soweit im Signaturenkatalog festgelegt, stehen für alle Layer Darstellungen in Farbe zur Verfügung. Für "Flurstücke" und "Gebäude" werden zusätzlich Darstellungen in Grausstufen (entsprechend Signaturenkatalog) und in Gelb (keine Flächendarstellung, nur Konturen) angeboten.</ows:Abstract>
+    <ows:Keywords>
+        <ows:Keyword>WMS</ows:Keyword>
+        <ows:Keyword>Landesamt für Vermessung ung Geoinformation Schleswig-Holstein</ows:Keyword>
+        <ows:Keyword>LVermGeo SH</ows:Keyword>
+        <ows:Keyword>AdV</ows:Keyword>
+        <ows:Keyword>ALKIS</ows:Keyword>
+        <ows:Keyword>opendata</ows:Keyword>
+    </ows:Keywords>
+    <ows:ServiceType>OGC WMTS</ows:ServiceType>
+    <ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion>
+    <ows:Fees>Für die Nutzung der Daten ist die Creative Commons (CC BY 4.0) – Namensnennung 4.0 International anzuwenden. Die Lizenz ist über  http://creativecommons.org/licenses/by/4.0 abrufbar. Der Quellenvermerk lautet "© GeoBasis-DE/LVermGeo SH/CC BY 4.0" ||{"id":"cc-by/4.0","name":"Creative Commons Namensnennung – 4.0 International (CC BY 4.0)","url":"http://creativecommons.org/licenses/by/4.0/","quelle":"© GeoBasis-DE/LVermGeo SH/CC BY 4.0"}</ows:Fees>
+    <ows:AccessConstraints>NONE</ows:AccessConstraints>
+  </ows:ServiceIdentification>
+  <ows:ServiceProvider>
+    <ows:ProviderName>Landesamt für Vermessung und Geoinformation Schleswig-Holstein (LVermGeo SH)</ows:ProviderName>
+    <ows:ProviderSite xlink:href="http://www.schleswig-holstein.de/DE/Landesregierung/LVERMGEOSH/lvermgeosh_node.html"/>
+    <ows:ServiceContact>
+      <ows:IndividualName>Servicestelle Geoserver</ows:IndividualName>
+      <ows:PositionName></ows:PositionName>
+      <ows:ContactInfo>
+        <ows:Phone>
+          <ows:Voice>+49 (0)431 383-2019</ows:Voice>
+          <ows:Facsimile>+49 (0)431 988624-2019</ows:Facsimile>
+        </ows:Phone>
+        <ows:Address>
+          <ows:DeliveryPoint>Landesamt für Vermessung und Geoinformation Schleswig-Holstein (LVermGeo SH)</ows:DeliveryPoint>
+          <ows:City>Kiel</ows:City>
+          <ows:PostalCode>24106</ows:PostalCode>
+          <ows:Country>Germany</ows:Country>
+          <ows:ElectronicMailAddress>Geoserver@LVermGeo.landsh.de</ows:ElectronicMailAddress>
+        </ows:Address>
+      </ows:ContactInfo>
+    </ows:ServiceContact>
+  </ows:ServiceProvider>
+  <Contents>
+    <Layer>
+      <ows:Title>SH_ALKIS</ows:Title>
+      <ows:Abstract></ows:Abstract>
+      <ows:WGS84BoundingBox>
+        <ows:LowerCorner>0.10594674240568917 45.237542736025574</ows:LowerCorner>
+        <ows:UpperCorner>20.448891294525673 56.84787345153812</ows:UpperCorner>
+      </ows:WGS84BoundingBox>
+      <ows:Identifier>SH_ALKIS</ows:Identifier>
+      <Style>
+        <ows:Identifier>default</ows:Identifier>
+        <LegendURL
+          format="image/png"
+          xlink:href="https://dienste.gdi-sh.de//WMTS_SH_ALKIS_OpenGBD/service?service=WMS&amp;request=GetLegendGraphic&amp;version=1.3.0&amp;format=image%2Fpng&amp;layer=SH_ALKIS"
+        />
+      </Style>
+      <Format>image/png</Format>
+      <TileMatrixSetLink>
+        <TileMatrixSet>DE_EPSG_25832_ADV</TileMatrixSet>
+      </TileMatrixSetLink>
+      <ResourceURL format="image/png" resourceType="tile"
+        template="https://dienste.gdi-sh.de//WMTS_SH_ALKIS_OpenGBD/wmts/SH_ALKIS/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png"/>
+    </Layer>
+    <TileMatrixSet>
+      <ows:Identifier>DE_EPSG_25832_ADV</ows:Identifier>
+      <ows:SupportedCRS>EPSG:25832</ows:SupportedCRS>
+      <TileMatrix>
+        <ows:Identifier>00</ows:Identifier>
+        <ScaleDenominator>4265.4591676995715</ScaleDenominator>
+        <TopLeftCorner>-46133.17 6301219.54</TopLeftCorner>
+        <TileWidth>256</TileWidth>
+        <TileHeight>256</TileHeight>
+        <MatrixWidth>4096</MatrixWidth>
+        <MatrixHeight>4096</MatrixHeight>
+      </TileMatrix>
+      <TileMatrix>
+        <ows:Identifier>01</ows:Identifier>
+        <ScaleDenominator>2132.729583849782</ScaleDenominator>
+        <TopLeftCorner>-46133.17 6301219.54</TopLeftCorner>
+        <TileWidth>256</TileWidth>
+        <TileHeight>256</TileHeight>
+        <MatrixWidth>8192</MatrixWidth>
+        <MatrixHeight>8192</MatrixHeight>
+      </TileMatrix>
+      <TileMatrix>
+        <ows:Identifier>02</ows:Identifier>
+        <ScaleDenominator>1066.3647919248929</ScaleDenominator>
+        <TopLeftCorner>-46133.17 6301219.54</TopLeftCorner>
+        <TileWidth>256</TileWidth>
+        <TileHeight>256</TileHeight>
+        <MatrixWidth>16384</MatrixWidth>
+        <MatrixHeight>16384</MatrixHeight>
+      </TileMatrix>
+    </TileMatrixSet>
+  </Contents>
+  <ServiceMetadataURL xlink:href="https://dienste.gdi-sh.de//WMTS_SH_ALKIS_OpenGBD/wmts/1.0.0/WMTSCapabilities.xml"/>
+</Capabilities>
\ No newline at end of file
diff --git a/tests/data/rdf.json b/tests/data/rdf.json
new file mode 100644
index 0000000000000000000000000000000000000000..21a9c10f88d2b005a0196595572967b7c5b05066
--- /dev/null
+++ b/tests/data/rdf.json
@@ -0,0 +1,273 @@
+[
+  {
+    "@id": "https://example.org/dataset/87e42608-769f-4ca8-8593-7546a027b2b8",
+    "@type": [
+      "http://www.w3.org/ns/dcat#Dataset"
+    ],
+    "http://purl.org/dc/terms/accessRights": [
+      {
+        "@id": "http://publications.europa.eu/resource/authority/access-right/PUBLIC"
+      }
+    ],
+    "http://purl.org/dc/terms/description": [
+      {
+        "@value": "Anzahl täglicher Landungen und Starts unbekannter Flugobjekte (UFOs) in Schleswig-Holstein.  🛸👽\n##Methodik\nGezählt werden nur die Landungen und Starts von UFOs, die gemeldet und zusätzlich offiziell bestätigt wurden. Sichtungen, die zu keinem Bodenkontakt führen, werden nicht gezählt.\n##Attribute\n- `datum` - Datum\n- `ufo_landungen` - Anzahl UFO-Landungen\n- `ufo_starts` - Anzahl UFO-Starts\n"
+      }
+    ],
+    "http://purl.org/dc/terms/identifier": [
+      {
+        "@value": "87e42608-769f-4ca8-8593-7546a027b2b8"
+      }
+    ],
+    "http://purl.org/dc/terms/issued": [
+      {
+        "@type": "http://www.w3.org/2001/XMLSchema#dateTime",
+        "@value": "2024-06-18T07:20:05.693344"
+      }
+    ],
+    "http://purl.org/dc/terms/license": [
+      {
+        "@id": "http://dcat-ap.de/def/licenses/cc-zero"
+      }
+    ],
+    "http://purl.org/dc/terms/modified": [
+      {
+        "@type": "http://www.w3.org/2001/XMLSchema#dateTime",
+        "@value": "2024-06-18T07:20:05.693344"
+      }
+    ],
+    "http://purl.org/dc/terms/publisher": [
+      {
+        "@id": "https://example.org/organization/ufo-kontrolle"
+      }
+    ],
+    "http://purl.org/dc/terms/spatial": [
+      {
+        "@id": "http://dcat-ap.de/def/politicalGeocoding/stateKey/01"
+      }
+    ],
+    "http://purl.org/dc/terms/temporal": [
+      {
+        "@id": "_:n1fa3c2476143497285348e0c39705837b1"
+      }
+    ],
+    "http://purl.org/dc/terms/title": [
+      {
+        "@value": "Bestätigte UFO-Landungen und -Starts"
+      }
+    ],
+    "http://www.w3.org/ns/dcat#distribution": [
+      {
+        "@id": "_:n1fa3c2476143497285348e0c39705837b4"
+      },
+      {
+        "@id": "_:n1fa3c2476143497285348e0c39705837b2"
+      }
+    ],
+    "http://www.w3.org/ns/dcat#keyword": [
+      {
+        "@value": "Weltall"
+      },
+      {
+        "@value": "Start"
+      },
+      {
+        "@value": "Landung"
+      },
+      {
+        "@value": "Raumschiff"
+      },
+      {
+        "@value": "UFO"
+      },
+      {
+        "@value": "Testdaten"
+      }
+    ],
+    "http://www.w3.org/ns/dcat#theme": [
+      {
+        "@id": "http://publications.europa.eu/resource/authority/data-theme/INTL"
+      }
+    ]
+  },
+  {
+    "@id": "_:n1fa3c2476143497285348e0c39705837b4",
+    "@type": [
+      "http://www.w3.org/ns/dcat#Distribution"
+    ],
+    "http://purl.org/dc/terms/format": [
+      {
+        "@id": "http://publications.europa.eu/resource/authority/file-type/JSON"
+      }
+    ],
+    "http://purl.org/dc/terms/issued": [
+      {
+        "@type": "http://www.w3.org/2001/XMLSchema#dateTime",
+        "@value": "2024-06-18T05:20:07.232559"
+      }
+    ],
+    "http://purl.org/dc/terms/license": [
+      {
+        "@id": "http://dcat-ap.de/def/licenses/cc-zero"
+      }
+    ],
+    "http://purl.org/dc/terms/modified": [
+      {
+        "@type": "http://www.w3.org/2001/XMLSchema#dateTime",
+        "@value": "2024-06-18T05:20:07.191976"
+      }
+    ],
+    "http://purl.org/dc/terms/rights": [
+      {
+        "@id": "http://dcat-ap.de/def/licenses/cc-zero"
+      }
+    ],
+    "http://purl.org/dc/terms/title": [
+      {
+        "@value": "Frictionless Data Resource"
+      }
+    ],
+    "http://spdx.org/rdf/terms#checksum": [
+      {
+        "@id": "_:n1fa3c2476143497285348e0c39705837b5"
+      }
+    ],
+    "http://www.w3.org/ns/dcat#accessURL": [
+      {
+        "@id": "http://localhost:8000/ufo-resource.json"
+      }
+    ],
+    "http://www.w3.org/ns/dcat#byteSize": [
+      {
+        "@type": "http://www.w3.org/2001/XMLSchema#integer",
+        "@value": 487
+      }
+    ],
+    "http://www.w3.org/ns/dcat#downloadURL": [
+      {
+        "@id": "http://localhost:8000/ufo-resource.json"
+      }
+    ],
+    "http://www.w3.org/ns/dcat#mediaType": [
+      {
+        "@id": "https://www.iana.org/assignments/media-types/application/csv"
+      }
+    ]
+  },
+  {
+    "@id": "_:n1fa3c2476143497285348e0c39705837b5",
+    "@type": [
+      "http://spdx.org/rdf/terms#Checksum"
+    ],
+    "http://spdx.org/rdf/terms#algorithm": [
+      {
+        "@id": "http://spdx.org/rdf/terms#checksumAlgorithm_md5"
+      }
+    ],
+    "http://spdx.org/rdf/terms#checksumValue": [
+      {
+        "@type": "http://www.w3.org/2001/XMLSchema#hexBinary",
+        "@value": "8dca8b179bbe0d46c5004da5112f6c4c"
+      }
+    ]
+  },
+  {
+    "@id": "_:n1fa3c2476143497285348e0c39705837b2",
+    "@type": [
+      "http://www.w3.org/ns/dcat#Distribution"
+    ],
+    "http://purl.org/dc/terms/format": [
+      {
+        "@id": "http://publications.europa.eu/resource/authority/file-type/CSV"
+      }
+    ],
+    "http://purl.org/dc/terms/issued": [
+      {
+        "@type": "http://www.w3.org/2001/XMLSchema#dateTime",
+        "@value": "2024-06-18T05:20:07.232559"
+      }
+    ],
+    "http://purl.org/dc/terms/license": [
+      {
+        "@id": "http://dcat-ap.de/def/licenses/cc-zero"
+      }
+    ],
+    "http://purl.org/dc/terms/modified": [
+      {
+        "@type": "http://www.w3.org/2001/XMLSchema#dateTime",
+        "@value": "2024-06-18T05:20:07.191976"
+      }
+    ],
+    "http://purl.org/dc/terms/rights": [
+      {
+        "@id": "http://dcat-ap.de/def/licenses/cc-zero"
+      }
+    ],
+    "http://purl.org/dc/terms/title": [
+      {
+        "@value": "ufo.csv"
+      }
+    ],
+    "http://spdx.org/rdf/terms#checksum": [
+      {
+        "@id": "_:n1fa3c2476143497285348e0c39705837b3"
+      }
+    ],
+    "http://www.w3.org/ns/dcat#accessURL": [
+      {
+        "@id": "http://localhost:8000/ufo.csv"
+      }
+    ],
+    "http://www.w3.org/ns/dcat#byteSize": [
+      {
+        "@type": "http://www.w3.org/2001/XMLSchema#integer",
+        "@value": 151
+      }
+    ],
+    "http://www.w3.org/ns/dcat#downloadURL": [
+      {
+        "@id": "http://localhost:8000/ufo.csv"
+      }
+    ],
+    "http://www.w3.org/ns/dcat#mediaType": [
+      {
+        "@id": "https://www.iana.org/assignments/media-types/application/csv"
+      }
+    ]
+  },
+  {
+    "@id": "_:n1fa3c2476143497285348e0c39705837b3",
+    "@type": [
+      "http://spdx.org/rdf/terms#Checksum"
+    ],
+    "http://spdx.org/rdf/terms#algorithm": [
+      {
+        "@id": "http://spdx.org/rdf/terms#checksumAlgorithm_sha1"
+      }
+    ],
+    "http://spdx.org/rdf/terms#checksumValue": [
+      {
+        "@type": "http://www.w3.org/2001/XMLSchema#hexBinary",
+        "@value": "3ffba0a43d3497a7918b376a335c31fbecc9325b"
+      }
+    ]
+  },
+  {
+    "@id": "_:n1fa3c2476143497285348e0c39705837b1",
+    "@type": [
+      "http://purl.org/dc/terms/PeriodOfTime"
+    ],
+    "http://www.w3.org/ns/dcat#endDate": [
+      {
+        "@type": "http://www.w3.org/2001/XMLSchema#date",
+        "@value": "2024-06-17"
+      }
+    ],
+    "http://www.w3.org/ns/dcat#startDate": [
+      {
+        "@type": "http://www.w3.org/2001/XMLSchema#date",
+        "@value": "2024-06-10"
+      }
+    ]
+  }
+]
\ No newline at end of file
diff --git a/tests/data/rdf.xml b/tests/data/rdf.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3e6690e5ad4d230b9dae318093040213914810bd
--- /dev/null
+++ b/tests/data/rdf.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<rdf:RDF
+   xmlns:dcat="http://www.w3.org/ns/dcat#"
+   xmlns:dcterms="http://purl.org/dc/terms/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:spdx="http://spdx.org/rdf/terms#"
+>
+  <rdf:Description rdf:about="https://example.org/dataset/87e42608-769f-4ca8-8593-7546a027b2b8">
+    <rdf:type rdf:resource="http://www.w3.org/ns/dcat#Dataset"/>
+    <dcterms:accessRights rdf:resource="http://publications.europa.eu/resource/authority/access-right/PUBLIC"/>
+    <dcterms:description>Anzahl täglicher Landungen und Starts unbekannter Flugobjekte (UFOs) in Schleswig-Holstein.  🛸👽
+##Methodik
+Gezählt werden nur die Landungen und Starts von UFOs, die gemeldet und zusätzlich offiziell bestätigt wurden. Sichtungen, die zu keinem Bodenkontakt führen, werden nicht gezählt.
+##Attribute
+- `datum` - Datum
+- `ufo_landungen` - Anzahl UFO-Landungen
+- `ufo_starts` - Anzahl UFO-Starts
+</dcterms:description>
+    <dcterms:identifier>87e42608-769f-4ca8-8593-7546a027b2b8</dcterms:identifier>
+    <dcterms:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2024-06-18T07:20:05.693344</dcterms:issued>
+    <dcterms:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2024-06-18T07:20:05.693344</dcterms:modified>
+    <dcterms:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero"/>
+    <dcterms:publisher rdf:resource="https://example.org/organization/ufo-kontrolle"/>
+    <dcterms:spatial rdf:resource="http://dcat-ap.de/def/politicalGeocoding/stateKey/01"/>
+    <dcterms:temporal rdf:nodeID="n6747fd43db2143cca14c39970555b181b1"/>
+    <dcterms:title>Bestätigte UFO-Landungen und -Starts</dcterms:title>
+    <dcat:distribution rdf:nodeID="n6747fd43db2143cca14c39970555b181b2"/>
+    <dcat:distribution rdf:nodeID="n6747fd43db2143cca14c39970555b181b4"/>
+    <dcat:keyword>UFO</dcat:keyword>
+    <dcat:keyword>Landung</dcat:keyword>
+    <dcat:keyword>Start</dcat:keyword>
+    <dcat:keyword>Raumschiff</dcat:keyword>
+    <dcat:keyword>Weltall</dcat:keyword>
+    <dcat:keyword>Testdaten</dcat:keyword>
+    <dcat:theme rdf:resource="http://publications.europa.eu/resource/authority/data-theme/INTL"/>
+  </rdf:Description>
+  <rdf:Description rdf:nodeID="n6747fd43db2143cca14c39970555b181b4">
+    <rdf:type rdf:resource="http://www.w3.org/ns/dcat#Distribution"/>
+    <dcterms:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/JSON"/>
+    <dcterms:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2024-06-18T05:20:07.232559</dcterms:issued>
+    <dcterms:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero"/>
+    <dcterms:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2024-06-18T05:20:07.191976</dcterms:modified>
+    <dcterms:rights rdf:resource="http://dcat-ap.de/def/licenses/cc-zero"/>
+    <dcterms:title>Frictionless Data Resource</dcterms:title>
+    <spdx:checksum rdf:nodeID="n6747fd43db2143cca14c39970555b181b5"/>
+    <dcat:accessURL rdf:resource="http://localhost:8000/ufo-resource.json"/>
+    <dcat:byteSize rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">487</dcat:byteSize>
+    <dcat:downloadURL rdf:resource="http://localhost:8000/ufo-resource.json"/>
+    <dcat:mediaType rdf:resource="https://www.iana.org/assignments/media-types/application/csv"/>
+  </rdf:Description>
+  <rdf:Description rdf:nodeID="n6747fd43db2143cca14c39970555b181b2">
+    <rdf:type rdf:resource="http://www.w3.org/ns/dcat#Distribution"/>
+    <dcterms:format rdf:resource="http://publications.europa.eu/resource/authority/file-type/CSV"/>
+    <dcterms:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2024-06-18T05:20:07.232559</dcterms:issued>
+    <dcterms:license rdf:resource="http://dcat-ap.de/def/licenses/cc-zero"/>
+    <dcterms:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2024-06-18T05:20:07.191976</dcterms:modified>
+    <dcterms:rights rdf:resource="http://dcat-ap.de/def/licenses/cc-zero"/>
+    <dcterms:title>ufo.csv</dcterms:title>
+    <spdx:checksum rdf:nodeID="n6747fd43db2143cca14c39970555b181b3"/>
+    <dcat:accessURL rdf:resource="http://localhost:8000/ufo.csv"/>
+    <dcat:byteSize rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">151</dcat:byteSize>
+    <dcat:downloadURL rdf:resource="http://localhost:8000/ufo.csv"/>
+    <dcat:mediaType rdf:resource="https://www.iana.org/assignments/media-types/application/csv"/>
+  </rdf:Description>
+  <rdf:Description rdf:nodeID="n6747fd43db2143cca14c39970555b181b5">
+    <rdf:type rdf:resource="http://spdx.org/rdf/terms#Checksum"/>
+    <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_md5"/>
+    <spdx:checksumValue rdf:datatype="http://www.w3.org/2001/XMLSchema#hexBinary">8dca8b179bbe0d46c5004da5112f6c4c</spdx:checksumValue>
+  </rdf:Description>
+  <rdf:Description rdf:nodeID="n6747fd43db2143cca14c39970555b181b3">
+    <rdf:type rdf:resource="http://spdx.org/rdf/terms#Checksum"/>
+    <spdx:algorithm rdf:resource="http://spdx.org/rdf/terms#checksumAlgorithm_sha1"/>
+    <spdx:checksumValue rdf:datatype="http://www.w3.org/2001/XMLSchema#hexBinary">3ffba0a43d3497a7918b376a335c31fbecc9325b</spdx:checksumValue>
+  </rdf:Description>
+  <rdf:Description rdf:nodeID="n6747fd43db2143cca14c39970555b181b1">
+    <rdf:type rdf:resource="http://purl.org/dc/terms/PeriodOfTime"/>
+    <dcat:endDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2024-06-17</dcat:endDate>
+    <dcat:startDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2024-06-10</dcat:startDate>
+  </rdf:Description>
+</rdf:RDF>
diff --git a/tests/data/valid.docx b/tests/data/valid.docx
new file mode 100644
index 0000000000000000000000000000000000000000..2fc6b99e13b6734528592b091b1fa76f3dbd8b2c
Binary files /dev/null and b/tests/data/valid.docx differ
diff --git a/tests/data/valid.ods b/tests/data/valid.ods
new file mode 100644
index 0000000000000000000000000000000000000000..3726fbeac976ef3a6a7399dd2db5682b88c513b4
Binary files /dev/null and b/tests/data/valid.ods differ
diff --git a/tests/data/valid.odt b/tests/data/valid.odt
new file mode 100644
index 0000000000000000000000000000000000000000..f4802adff3a9191037421a3ccd64151e95d8fe1f
Binary files /dev/null and b/tests/data/valid.odt differ
diff --git a/tests/data/valid.xlsx b/tests/data/valid.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..5f1a3bcdf0411c4b605d5fa51708daf42dc48ae7
Binary files /dev/null and b/tests/data/valid.xlsx differ
diff --git a/tests/test_all_formats.py b/tests/test_all_formats.py
new file mode 100644
index 0000000000000000000000000000000000000000..b2b8fd9d0d51797a984b6d255509c4839da219c8
--- /dev/null
+++ b/tests/test_all_formats.py
@@ -0,0 +1,26 @@
+import unittest
+import importlib
+import pkgutil
+import tempfile
+
+
+class TestAllFormats(unittest.TestCase):
+    def test_load_all_modules(self):
+        """Make sure that every format module has been loaded at least once.
+        Otherwise, the code coverage will not know about the file."""
+        package = importlib.import_module("formats")
+        modules = [module.name for module in pkgutil.iter_modules(package.__path__)]
+        for module in modules:
+            format_check_module = importlib.import_module("formats." + module)
+            with tempfile.NamedTemporaryFile(delete=True) as temp_file:
+                resource = {}
+                resource["url"] = "https://test.invalid/data"
+                try:
+                    format_check_module.is_valid(resource, temp_file)
+                except Exception as e:
+                    print(f"Module for format {module} failed.")
+                    raise (e)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tests/test_format_fidelity_checker.py b/tests/test_format_fidelity_checker.py
index 6c21860f2aab0768936f1b38a649221aebde3a11..f5798a751b3f808694ef6f8bb1f63736aaaaa775 100644
--- a/tests/test_format_fidelity_checker.py
+++ b/tests/test_format_fidelity_checker.py
@@ -11,10 +11,9 @@ from rdflib.namespace import RDF, DCAT
 
 class TestDcatCatalogCheck(unittest.TestCase):
     def setUp(self):
-        self.dcc = DcatCatalogCheck(
-            "http://localhost:8000/", "my_api_key")
+        self.dcc = DcatCatalogCheck("http://test.invalid:8000/", "my_api_key")
         # Mock the logger to capture log messages
-        self.logger_patch = patch.object(self.dcc, 'logger', MagicMock())
+        self.logger_patch = patch.object(self.dcc, "logger", MagicMock())
         self.mock_logger = self.logger_patch.start()
 
     def tearDown(self):
@@ -30,13 +29,10 @@ class TestDcatCatalogCheck(unittest.TestCase):
             "XML": ["application/xml"],
         }
 
-        self.assertTrue(self.dcc.is_mime_type_compatible(
-            "JSON", "application/json"))
-        self.assertFalse(self.dcc.is_mime_type_compatible(
-            "JSON", "application/xml"))
+        self.assertTrue(self.dcc.is_mime_type_compatible("JSON", "application/json"))
+        self.assertFalse(self.dcc.is_mime_type_compatible("JSON", "application/xml"))
         self.assertFalse(
-            self.dcc.is_mime_type_compatible(
-                "UnknownFormat", "application/json")
+            self.dcc.is_mime_type_compatible("UnknownFormat", "application/json")
         )
 
     def test_read_allowed_file_formats(self):
@@ -48,8 +44,7 @@ class TestDcatCatalogCheck(unittest.TestCase):
         ):
             formats = self.dcc.read_allowed_file_formats()
             self.assertEqual(
-                formats, {"JSON": ["application/json"],
-                          "XML": ["application/xml"]}
+                formats, {"JSON": ["application/json"], "XML": ["application/xml"]}
             )
 
     def test_load_uri_replacements(self):
@@ -59,10 +54,8 @@ class TestDcatCatalogCheck(unittest.TestCase):
                 read_data='[{"regex": "old", "replaced_by": "new"}]'
             ),
         ):
-
             replacements = self.dcc.load_uri_replacements()
-            self.assertEqual(
-                replacements, [{"regex": "old", "replaced_by": "new"}])
+            self.assertEqual(replacements, [{"regex": "old", "replaced_by": "new"}])
 
     # Simulate that the file does not exist
 
@@ -111,7 +104,7 @@ class TestDcatCatalogCheck(unittest.TestCase):
         self.dcc.load_http_complete = MagicMock(return_value=mock_response)
 
         resource = {}
-        resource["url"] = "http://localhost/data"
+        resource["url"] = "http://test.invalid/data"
         resource["format"] = "JSON"
         self.dcc.check_resource(resource)
         self.assertEqual(resource["accessible"], True)
@@ -128,7 +121,7 @@ class TestDcatCatalogCheck(unittest.TestCase):
         self.dcc.load_http_complete = MagicMock(return_value=mock_response)
 
         resource = {}
-        resource["url"] = "http://localhost/data"
+        resource["url"] = "http://test.invalid/data"
         resource["format"] = "JSON"
         self.dcc.check_resource(resource)
         self.assertEqual(resource["accessible"], True)
@@ -146,7 +139,7 @@ class TestDcatCatalogCheck(unittest.TestCase):
         self.dcc.load_http_complete = MagicMock(return_value=mock_response)
 
         resource = {}
-        resource["url"] = "http://localhost/data"
+        resource["url"] = "http://test.invalid/data"
         resource["format"] = "JSON"
         self.dcc.check_resource(resource)
         self.assertEqual(resource["accessible"], True)
@@ -164,7 +157,7 @@ class TestDcatCatalogCheck(unittest.TestCase):
         self.dcc.load_http_complete = MagicMock(return_value=mock_response)
 
         resource = {}
-        resource["url"] = "http://localhost/data"
+        resource["url"] = "http://test.invalid/data"
         resource["format"] = "JSON"
         self.dcc.check_resource(resource)
         self.assertEqual(resource["accessible"], True)
@@ -182,7 +175,7 @@ class TestDcatCatalogCheck(unittest.TestCase):
         self.dcc.load_http_complete = MagicMock(return_value=mock_response)
 
         resource = {}
-        resource["url"] = "http://localhost/data"
+        resource["url"] = "http://test.invalid/data"
         resource["format"] = "JSON"
         self.dcc.check_resource(resource)
         self.assertEqual(resource["accessible"], True)
@@ -198,7 +191,7 @@ class TestDcatCatalogCheck(unittest.TestCase):
         self.dcc.load_http_complete = MagicMock(return_value=mock_response)
 
         resource = {}
-        resource["url"] = "http://localhost/data"
+        resource["url"] = "http://test.invalid/data"
         resource["format"] = "XML"
         self.dcc.check_resource(resource)
         self.assertEqual(resource["accessible"], True)
@@ -214,7 +207,7 @@ class TestDcatCatalogCheck(unittest.TestCase):
         self.dcc.load_http_complete = MagicMock(return_value=mock_response)
 
         resource = {}
-        resource["url"] = "http://localhost/data"
+        resource["url"] = "http://test.invalid/data"
         resource["format"] = "PNG"
         resource["checksum_algorithm"] = (
             "http://spdx.org/rdf/terms#checksumAlgorithm_sha1"
@@ -247,7 +240,7 @@ class TestDcatCatalogCheck(unittest.TestCase):
         self.dcc.load_http_complete = MagicMock(return_value=mock_response)
 
         resource = {}
-        resource["url"] = "http://localhost/data"
+        resource["url"] = "http://test.invalid/data"
         resource["format"] = "JSON"
         self.dcc.check_resource(resource)
         self.assertEqual(resource.get("accessible"), True)
@@ -266,7 +259,7 @@ class TestDcatCatalogCheck(unittest.TestCase):
         self.dcc.load_http_complete = MagicMock(return_value=mock_response)
 
         resource = {}
-        resource["url"] = "http://localhost/data"
+        resource["url"] = "http://test.invalid/data"
         resource["format"] = "JSON"
         self.dcc.check_resource(resource)
         self.assertEqual(resource.get("accessible"), True)
@@ -285,7 +278,7 @@ class TestDcatCatalogCheck(unittest.TestCase):
         self.dcc.load_http_complete = MagicMock(return_value=mock_response)
 
         resource = {}
-        resource["url"] = "http://localhost/data"
+        resource["url"] = "http://test.invalid/data"
         resource["format"] = "JSON"
         self.dcc.check_resource(resource)
         self.assertEqual(resource.get("accessible"), True)
@@ -312,7 +305,7 @@ class TestDcatCatalogCheck(unittest.TestCase):
         self.dcc.load_http_complete = MagicMock(return_value=mock_response)
 
         resource = {}
-        resource["url"] = "http://localhost/zos116.zip"
+        resource["url"] = "http://test.invalid/zos116.zip"
         resource["format"] = "SHP"
 
         self.dcc.check_resource(resource)
@@ -326,7 +319,7 @@ class TestDcatCatalogCheck(unittest.TestCase):
         # Test data to simulate the contents of previous_results.json
         test_data = [
             {"url": "http://example.com", "status": "valid", "format": "JSON"},
-            {"url": "http://example.org", "status": "invalid", "format": "XML"}
+            {"url": "http://example.org", "status": "invalid", "format": "XML"},
         ]
 
         # Write test data to a file 'previous_results.json'
@@ -342,9 +335,11 @@ class TestDcatCatalogCheck(unittest.TestCase):
         self.assertIn("http://example.com", self.dcc.previous_results)
         self.assertIn("http://example.org", self.dcc.previous_results)
         self.assertEqual(
-            self.dcc.previous_results["http://example.com"]["status"], "valid")
+            self.dcc.previous_results["http://example.com"]["status"], "valid"
+        )
         self.assertEqual(
-            self.dcc.previous_results["http://example.org"]["status"], "invalid")
+            self.dcc.previous_results["http://example.org"]["status"], "invalid"
+        )
 
     @patch("os.path.exists", return_value=False)
     def test_read_previous_results_file_not_exist(self, mock_exists):
@@ -365,7 +360,12 @@ class TestDcatCatalogCheck(unittest.TestCase):
             "Invalid JSON at line 1: Expecting value: line 1 column 1 (char 0)"
         )
 
-    @patch("builtins.open", mock_open(read_data='{"status": "valid", "format": "JSON"}\n{"url": "http://example.com", "status": "valid", "format": "JSON"}'))
+    @patch(
+        "builtins.open",
+        mock_open(
+            read_data='{"status": "valid", "format": "JSON"}\n{"url": "http://example.com", "status": "valid", "format": "JSON"}'
+        ),
+    )
     @patch("os.path.exists", return_value=True)
     def test_read_previous_results_missing_url(self, mock_exists):
         """Test when the file has a line with missing 'url'."""
diff --git a/tests/test_gml_format.py b/tests/test_gml_format.py
index a2e40a7d8820ae251db773c05721cc93cd5bd34b..e63638cfa5c2e4616dfc334b28432f25592ab9a2 100644
--- a/tests/test_gml_format.py
+++ b/tests/test_gml_format.py
@@ -7,11 +7,13 @@ class TestGmlFormat(unittest.TestCase):
         resource = {}
         with open("tests/data/bermuda.gml", "r") as file:
             self.assertTrue(is_valid(resource, file))
+            self.assertIsNone(resource.get("error"))
 
     def test_is_valid__invalid(self):
         resource = {}
         with open("tests/data/correct.xml", "r") as file:
             self.assertFalse(is_valid(resource, file))
+            self.assertIsNotNone(resource.get("error"))
 
 
 if __name__ == "__main__":
diff --git a/tests/test_ods_format.py b/tests/test_ods_format.py
new file mode 100644
index 0000000000000000000000000000000000000000..e152d5eddf08627d001a69d4f1ff7a11146683d8
--- /dev/null
+++ b/tests/test_ods_format.py
@@ -0,0 +1,36 @@
+import unittest
+from formats.ods_format import is_valid
+
+
+class TestOdsFormat(unittest.TestCase):
+    def test_is_valid__valid(self):
+        resource = {}
+        with open("tests/data/valid.ods", "r") as file:
+            self.assertTrue(is_valid(resource, file))
+            self.assertIsNone(resource.get("error"))
+
+    def test_is_valid__invalid_no_zip(self):
+        resource = {}
+        with open("tests/data/correct.json", "r") as file:
+            self.assertFalse(is_valid(resource, file))
+            self.assertIsNotNone(resource.get("error"))
+
+    def test_is_valid__invalid_no_odt(self):
+        resource = {}
+        with open("tests/data/valid.odt", "r") as file:
+            self.assertFalse(is_valid(resource, file))
+            self.assertIsNotNone(resource.get("error"))
+            self.assertEqual(
+                "Incorrect MIME type: application/vnd.oasis.opendocument.text",
+                resource["error"],
+            )
+
+    def test_is_valid__invalid_zip(self):
+        resource = {}
+        with open("tests/data/valid.xlsx", "r") as file:
+            self.assertFalse(is_valid(resource, file))
+            self.assertIsNotNone(resource.get("error"))
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tests/test_rdf_format.py b/tests/test_rdf_format.py
new file mode 100644
index 0000000000000000000000000000000000000000..8a313f36f32b899de757e64ee65085addaa69295
--- /dev/null
+++ b/tests/test_rdf_format.py
@@ -0,0 +1,32 @@
+import unittest
+from formats.rdf_format import is_valid
+
+
+class TestRdfFormat(unittest.TestCase):
+    def test_is_valid__valid_turtle(self):
+        resource = {}
+        with open("tests/data/ufo.ttl", "r") as file:
+            self.assertTrue(is_valid(resource, file))
+            self.assertIsNone(resource.get("error"))
+
+    def test_is_valid__valid_xml(self):
+        resource = {}
+        with open("tests/data/rdf.xml", "r") as file:
+            self.assertTrue(is_valid(resource, file))
+            self.assertIsNone(resource.get("error"))
+
+    def test_is_valid__valid_jsonld(self):
+        resource = {}
+        with open("tests/data/rdf.json", "r") as file:
+            self.assertTrue(is_valid(resource, file))
+            self.assertIsNone(resource.get("error"))
+
+    def test_is_valid__invalid(self):
+        resource = {}
+        with open("tests/data/correct.json", "r") as file:
+            self.assertFalse(is_valid(resource, file))
+            self.assertIsNotNone(resource.get("error"))
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tests/test_wmts_format.py b/tests/test_wmts_format.py
new file mode 100644
index 0000000000000000000000000000000000000000..9301e22c9092ab52f00277c1b23314ac6663c28f
--- /dev/null
+++ b/tests/test_wmts_format.py
@@ -0,0 +1,26 @@
+import unittest
+from formats.wmts_srvc_format import is_valid
+
+
+class TestWmtsSrvcFormat(unittest.TestCase):
+    def test_is_valid__valid(self):
+        resource = {}
+        resource["url"] = (
+            "https://dienste.gdi-sh.invalid/WMTS_SH_ALKIS_OpenGBD/wmts/1.0.0/WMTSCapabilities.xml"
+        )
+        with open("tests/data/WMTSCapabilities.xml", "r") as file:
+            self.assertTrue(is_valid(resource, file))
+            self.assertIsNone(resource.get("error"))
+
+    def test_is_valid__invalid(self):
+        resource = {}
+        resource["url"] = (
+            "https://dienste.gdi-sh.invalid/WMTS_SH_ALKIS_OpenGBD/wmts/1.0.0/WMTSCapabilities.xml"
+        )
+        with open("tests/data/correct.xml", "r") as file:
+            self.assertFalse(is_valid(resource, file))
+            self.assertIsNotNone(resource.get("error"))
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tests/test_xml_format.py b/tests/test_xml_format.py
new file mode 100644
index 0000000000000000000000000000000000000000..5672f9f4e6ef3342e443d960dcb01090f28015a9
--- /dev/null
+++ b/tests/test_xml_format.py
@@ -0,0 +1,20 @@
+import unittest
+from formats.xml_format import is_valid
+
+
+class TestXmlFormat(unittest.TestCase):
+    def test_is_valid__valid(self):
+        resource = {}
+        with open("tests/data/correct.xml", "r") as file:
+            self.assertTrue(is_valid(resource, file))
+            self.assertIsNone(resource.get("error"))
+
+    def test_is_valid__invalid(self):
+        resource = {}
+        with open("tests/data/incorrect.xml", "r") as file:
+            self.assertFalse(is_valid(resource, file))
+            self.assertIsNotNone(resource.get("error"))
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tests/test_zip_format.py b/tests/test_zip_format.py
new file mode 100644
index 0000000000000000000000000000000000000000..61617376b0a56cfec1b5c75f61d9f65d128286ae
--- /dev/null
+++ b/tests/test_zip_format.py
@@ -0,0 +1,21 @@
+import unittest
+from formats.zip_format import is_valid
+
+
+class TestZipFormat(unittest.TestCase):
+    def test_is_valid__valid(self):
+        resource = {}
+        with open("tests/data/bermuda.zip", "r") as file:
+            self.assertTrue(is_valid(resource, file))
+            self.assertIsNone(resource.get("error"))
+
+    def test_is_valid__invalid(self):
+        resource = {}
+        with open("tests/data/correct.xml", "r") as file:
+            self.assertFalse(is_valid(resource, file))
+            self.assertIsNotNone(resource.get("error"))
+            self.assertEqual("Not a ZIP file.", resource["error"])
+
+
+if __name__ == "__main__":
+    unittest.main()