diff --git a/integrationtests/README.md b/integrationtests/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..c1f96606a46de4dd96f90fd4a1e46957100e68b3
--- /dev/null
+++ b/integrationtests/README.md
@@ -0,0 +1,3 @@
+1. Clear database
+2. Insert model
+3. Run test.py
diff --git a/integrationtests/clear_database.py b/integrationtests/clear_database.py
new file mode 100644
index 0000000000000000000000000000000000000000..138cf4e6abb256d5710cd2b32f55a1fb51f3fbed
--- /dev/null
+++ b/integrationtests/clear_database.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+#
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2020 Indiscale GmbH <info@indiscale.com>
+# Copyright (C) 2020 Florian Spreckelsen <f.spreckelsen@indiscale.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with this program. If not, see
+# <https://www.gnu.org/licenses/>.
+#
+# ** end header
+#
+"""Clear the database before and after the integration tests."""
+import caosdb as db
+
+
+def clear_all():
+    """First remove Records, then RecordTypes, then Properties, finally
+    files. Since there may be no entities, execute all deletions
+    without raising errors.
+
+    """
+    db.execute_query("FIND Record").delete(
+        raise_exception_on_error=False)
+    db.execute_query("FIND RecordType").delete(
+        raise_exception_on_error=False)
+    db.execute_query("FIND Property").delete(
+        raise_exception_on_error=False)
+    db.execute_query("FIND File").delete(
+        raise_exception_on_error=False)
+
+
+if __name__ == "__main__":
+    clear_all()
diff --git a/integrationtests/insert_model.py b/integrationtests/insert_model.py
new file mode 100755
index 0000000000000000000000000000000000000000..45bdb6c837c36c999b289548e0f685519cd3aa85
--- /dev/null
+++ b/integrationtests/insert_model.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+# encoding: utf-8
+#
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2021 Henrik tom Wörden
+#               2021 Alexander Schlemmer
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+import caosdb as db
+from caosadvancedtools.models.data_model import DataModel
+from caosadvancedtools.models.parser import parse_model_from_yaml
+
+
+def main():
+    model = parse_model_from_yaml("model.yml")
+    model.sync_data_model(noquestion=True)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/integrationtests/model.yml b/integrationtests/model.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7d78ac7ef4bc792f54594b29a8ac311479f41a59
--- /dev/null
+++ b/integrationtests/model.yml
@@ -0,0 +1,88 @@
+Experiment:
+  obligatory_properties:
+    date:
+      datatype: DATETIME 
+      description: 'date of the experiment'
+    identifier:
+      datatype: TEXT 
+      description: 'identifier of the experiment'
+  # TODO empty  recommended_properties is a problem
+  #recommended_properties:
+    responsible:
+      datatype: LIST<Person>
+Project:
+SoftwareVersion:
+  recommended_properties:
+    version:
+      datatype: TEXT 
+      description: 'Version of the software.'
+    binaries:
+    sourceCode:
+    Software:
+DepthTest:
+  obligatory_properties:
+    temperature:
+      datatype: DOUBLE 
+      description: 'temp'
+    depth:
+      datatype: DOUBLE 
+      description: 'temp'
+Person:
+  obligatory_properties:
+    first_name:
+      datatype: TEXT 
+      description: 'First name of a Person.'
+    last_name:
+      datatype: TEXT 
+      description: 'LastName of a Person.'
+  recommended_properties:
+    email:
+      datatype: TEXT 
+      description: 'Email of a Person.'
+revisionOf:
+  datatype: REFERENCE
+results:
+  datatype: LIST<REFERENCE>
+sources:
+  datatype: LIST<REFERENCE>
+scripts:
+  datatype: LIST<REFERENCE>
+single_attribute:
+  datatype: LIST<INTEGER>
+Simulation:
+  obligatory_properties:
+    date:
+    identifier:
+    responsible:
+Analysis:
+  obligatory_properties:
+    date:
+    identifier:
+    responsible:
+  suggested_properties:
+    mean_value:
+      datatype: DOUBLE
+Publication:
+Thesis:
+  inherit_from_suggested:
+  - Publication
+Article:
+  inherit_from_suggested:
+  - Publication
+Poster:
+  inherit_from_suggested:
+  - Publication
+Presentation:
+  inherit_from_suggested:
+  - Publication
+Report:
+  inherit_from_suggested:
+  - Publication
+hdf5File:
+  datatype: REFERENCE
+extern:
+  - TestRT1
+  - TestP1
+Measurement:
+  recommended_properties:
+    date:
diff --git a/integrationtests/test.py b/integrationtests/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..782687be27863e479186717d698b9965f7be8c64
--- /dev/null
+++ b/integrationtests/test.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+# encoding: utf-8
+#
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2021 Indiscale GmbH <info@indiscale.com>
+#               2021 Henrik tom Wörden <h.tomwoerden@indiscale.com>
+#               2021 Alexander Schlemmer
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# ** end header
+#
+
+"""
+module description
+"""
+
+import argparse
+import sys
+from argparse import RawTextHelpFormatter
+from newcrawler import Crawler
+from unittest.mock import Mock
+import caosdb as db
+from newcrawler.identifiable_adapters import CaosDBIdentifiableAdapter
+
+import os
+
+
+def rfp(*pathcomponents):
+    """
+    Return full path.
+    Shorthand convenience function.
+    """
+    return os.path.join(os.path.dirname(__file__), *pathcomponents)
+
+
+def main(args):
+    ident_adapt = CaosDBIdentifiableAdapter()
+    # TODO place this definition of identifiables elsewhere
+    ident_adapt.register_identifiable(
+        "Person", db.RecordType()
+        .add_parent(name="Person")
+        .add_property(name="first_name")
+        .add_property(name="last_name"))
+    ident_adapt.register_identifiable(
+        "Measurement", db.RecordType()
+        .add_parent(name="Measurement")
+        .add_property(name="identifier")
+        .add_property(name="date")
+        .add_property(name="project"))
+    ident_adapt.register_identifiable(
+        "Project", db.RecordType()
+        .add_parent(name="Project")
+        .add_property(name="date")
+        .add_property(name="identifier"))
+
+    crawler = Crawler(debug=True, identifiableAdapter=ident_adapt)
+    crawler.copy_attributes = Mock()
+    crawler.crawl_directory(rfp("../unittests/test_directories", "examples_article"),
+                            rfp("../unittests/scifolder_cfood.yml"))
+    ins, ups = crawler.synchronize()
+    assert len(ins) == 18
+    assert len(ups) == 0
+
+
+def parse_args():
+    parser = argparse.ArgumentParser(description=__doc__,
+                                     formatter_class=RawTextHelpFormatter)
+    # parser.add_argument("path",
+    #                    help="the subtree of files below the given path will "
+    #                    "be considered. Use '/' for everything.")
+
+    return parser.parse_args()
+
+
+if __name__ == "__main__":
+    args = parse_args()
+    sys.exit(main(args))
diff --git a/src/newcrawler/crawl.py b/src/newcrawler/crawl.py
index 17833eea4017ad1846dcddf9310918f781e446c8..be2a6792ac29dd7ee565bae2de66aaaa8be7f1dc 100644
--- a/src/newcrawler/crawl.py
+++ b/src/newcrawler/crawl.py
@@ -301,8 +301,21 @@ class Crawler(object):
                     # This record is a duplicate that can be removed. Make sure we do not lose
                     # information
                     # Update an (local) identified record that will be inserted
+                    newrecord = self.get_identified_record_from_local_cache(record)
                     self.copy_attributes(
-                        fro=record, to=self.get_identified_record_from_local_cache(record))
+                        fro=record, to=newrecord)
+                    # Bend references to the other object
+                    # TODO refactor this
+                    for el in flat + to_be_inserted + to_be_updated:
+                        for p in el.properties:
+                            if isinstance(p.value, list):
+                                for index, val in enumerate(p.value):
+                                    if val is record:
+                                        p.value[index] = newrecord
+                            else:
+                                if p.value is record:
+                                    p.value = newrecord
+
                     del flat[i]
                     continue
 
@@ -346,15 +359,23 @@ class Crawler(object):
                     if isinstance(val, db.Entity):
                         el.value[index] = val.id
 
-    def remove_unnecessary_updates(self, updateList: list[db.Record]):
+    @staticmethod
+    def remove_unnecessary_updates(updateList: list[db.Record],
+                                   identified_records: list[db.Record]):
         """
         checks whether all relevant attributes (especially Property values) are equal
+
+        Returns (in future)
+        -------
+        update list without unecessary updates
+
         """
+        if len(updateList) != len(identified_records):
+            raise RuntimeError("The lists of updates and of identified records need to be of the "
+                               "same length!")
+        # TODO this can now easily be changed to a function without side effect
         for i in reversed(range(len(updateList))):
-            record = updateList[i]
-            identifiable = self.identifiableAdapter.retrieve_identifiable(record)
-
-            comp = compare_entities(record, identifiable)
+            comp = compare_entities(updateList[i], identified_records[i])
             identical = True
             for j in range(2):
                 # TODO: should be implemented elsewhere (?)
@@ -366,12 +387,15 @@ class Crawler(object):
                     break
             for key in comp[0]["properties"]:
                 for attribute in ("datatype", "importance", "unit"):
-                    if (attribute in comp[0]["properties"][key] and
-                        comp[0]["properties"][key][attribute] is not None and
-                        comp[0]["properties"][key][attribute] !=
-                            comp[1]["properties"][key][attribute]):
-                        identical = False
-                        break
+                    # only make an update for those attributes if there is a value difference and
+                    # the value in the updateList is not None
+                    if attribute in comp[0]["properties"][key]:
+                        attr_val = comp[0]["properties"][key][attribute]
+                        other_attr_val = (comp[1]["properties"][key][attribute]
+                                          if attribute in comp[1]["properties"][key] else None)
+                        if attr_val is not None and atrr_val != other_attr_val:
+                            identical = False
+                            break
 
                 if "value" in comp[0]["properties"][key]:
                     identical = False
@@ -385,6 +409,16 @@ class Crawler(object):
             else:
                 pass
 
+    @staticmethod
+    def execute_inserts_in_list(to_be_inserted):
+        if len(to_be_inserted) > 0:
+            db.Container().extend(to_be_inserted).insert()
+
+    @staticmethod
+    def execute_updates_in_list(to_be_updated):
+        if len(to_be_updated) > 0:
+            db.Container().extend(to_be_updated).update()
+
     def _synchronize(self, updateList: list[db.Record]):
         """
         This function applies several stages:
@@ -407,11 +441,12 @@ class Crawler(object):
         for el in to_be_updated:
             self.replace_entities_by_ids(el)
 
-        self.remove_unnecessary_updates(to_be_updated)
+        identified_records = [self.identifiableAdapter.retrieve_identifiable(record) for record
+                              in to_be_updated]
+        self.remove_unnecessary_updates(to_be_updated, identified_records)
 
-        # TODO
-        # self.execute_inserts_in_list(to_be_inserted)
-        # self.execute_updates_in_list(to_be_updated)
+        self.execute_inserts_in_list(to_be_inserted)
+        self.execute_updates_in_list(to_be_updated)
 
         return (to_be_inserted, to_be_updated)
 
diff --git a/src/newcrawler/identifiable_adapters.py b/src/newcrawler/identifiable_adapters.py
index 01eb55bbb3ec4b54a49633ef839b2ed99ab5b398..f11a7fc101225db4fd3bdd15f3ad397425930d08 100644
--- a/src/newcrawler/identifiable_adapters.py
+++ b/src/newcrawler/identifiable_adapters.py
@@ -23,9 +23,34 @@
 # ** end header
 #
 
+from datetime import datetime
 import caosdb as db
 from abc import abstractmethod
 from .utils import get_value, has_parent
+from caosdb.common.datatype import is_reference
+from .utils import has_parent
+
+
+def convert_value(value):
+    """ Returns a string representation of the value that is suitable to be used in the query
+    looking for the identified record.
+
+    Parameters
+    ----------
+    value : The property of which the value shall be returned.
+
+    Returns
+    -------
+    out : the string reprensentation of the value
+
+    """
+
+    if isinstance(value, db.Entity):
+        return str(value.id)
+    elif isinstance(value, datetime):
+        return value.isoformat()
+    else:
+        return str(value)
 
 
 class IdentifiableAdapter(object):
@@ -67,7 +92,6 @@ class IdentifiableAdapter(object):
         if len(ident.parents) != 1:
             raise RuntimeError("Multiple parents for identifiables not supported.")
 
-        # TODO prevent multiple parents
         query_string = "FIND Record " + ident.get_parents()[0].name
         query_string += " WITH "
 
@@ -76,17 +100,16 @@ class IdentifiableAdapter(object):
                 "The identifiable must have features to identify it.")
 
         if ident.name is not None:
-            query_string += "name='{}' AND".format(ident.name)
+            query_string += "name='{}' AND ".format(ident.name)
 
         for p in ident.get_properties():
-            # TODO this is badly wrong :-|
-
-            if p.datatype is not None and p.datatype.startswith("LIST<"):
+            if isinstance(p.value, list):
                 for v in p.value:
-                    query_string += ("references " + str(v.id if isinstance(v, db.Entity)
-                                                         else v) + " AND ")
+                    query_string += ("'" + p.name + "'='" +
+                                     convert_value(v) + "' AND ")
             else:
-                query_string += ("'" + p.name + "'='" + str(get_value(p)) + "' AND ")
+                query_string += ("'" + p.name + "'='" +
+                                 convert_value(p.value) + "' AND ")
         # remove the last AND
         return query_string[:-4]
 
@@ -231,7 +254,7 @@ class LocalStorageIdentifiableAdapter(IdentifiableAdapter):
 
     def get_registered_identifiable(self, record: db.Record):
         identifiable_candidates = []
-        for name, definition in self._registered_identifiables.items():
+        for _, definition in self._registered_identifiables.items():
             if self.is_identifiable_for_record(definition, record):
                 identifiable_candidates.append(definition)
         if len(identifiable_candidates) > 1:
@@ -303,3 +326,39 @@ class LocalStorageIdentifiableAdapter(IdentifiableAdapter):
             raise RuntimeError("The entity has not been assigned an ID.")
 
         return value_identifiable.id
+
+
+class CaosDBIdentifiableAdapter(IdentifiableAdapter):
+    """
+    Identifiable adapter which can be used for production.
+
+
+    TODO: store registred identifiables not locally
+    """
+
+    def __init__(self):
+        self._registered_identifiables = dict()
+
+    def register_identifiable(self, name: str, definition: db.RecordType):
+        self._registered_identifiables[name] = definition
+
+    def get_registered_identifiable(self, record: db.Record):
+        """
+        returns the registred identifiable for the given Record
+
+        It is assumed, that there is exactly one identifiable for each RecordType. Only the first
+        parent of the given Record is considered; others are ignored
+        """
+        rt_name = record.parents[0].name
+        for name, definition in self._registered_identifiables.items():
+            if definition.parents[0].name.lower() == rt_name.lower():
+                return definition
+
+    def retrieve_identified_record(self, identifiable: db.Record):
+        query_string = self.create_query_for_identifiable(identifiable)
+        candidates = db.execute_query(query_string)
+        if len(candidates) > 1:
+            raise RuntimeError("Identifiable was not defined unambigiously.")
+        if len(candidates) == 0:
+            return None
+        return candidates[0]
diff --git a/src/newcrawler/utils.py b/src/newcrawler/utils.py
index c60b7f871db32d66c12781e5f0cfb246bc41c8fe..35fefe6719d579bc8e8a39489f8a872c0cca11b8 100644
--- a/src/newcrawler/utils.py
+++ b/src/newcrawler/utils.py
@@ -40,27 +40,3 @@ def has_parent(entity: db.Entity, name: str):
         if parent.name == name:
             return True
     return False
-
-
-def get_value(prop):
-    """ Returns the value of a Property
-
-    This function is taken from the old crawler:
-    caosdb-advanced-user-tools/src/caosadvancedtools/crawler.py
-
-    Parameters
-    ----------
-    prop : The property of which the value shall be returned.
-
-    Returns
-    -------
-    out : The value of the property; if the value is an entity, its ID.
-
-    """
-
-    if isinstance(prop.value, db.Entity):
-        return prop.value.id
-    elif isinstance(prop.value, datetime):
-        return prop.value.isoformat()
-    else:
-        return prop.value
diff --git a/unittests/test_converters.py b/unittests/test_converters.py
index ab8107398a3fbd2a27e3d174d5bc892ec7d8af1e..3ec1764631c4de7b5a7cc247cc559d0dc5f5939c 100644
--- a/unittests/test_converters.py
+++ b/unittests/test_converters.py
@@ -29,6 +29,7 @@ test the converters module
 
 from newcrawler.converters import Converter
 from newcrawler.stores import GeneralStore
+from newcrawler.converters import MarkdownFileConverter
 from newcrawler.structure_elements import Directory
 
 from test_tool import rfp
@@ -65,3 +66,57 @@ def testDirectoryConverter():
     assert len(elements) == 1
     assert isinstance(elements[0], Directory)
     assert elements[0].name == "examples_article"
+
+
+def test_markdown_converter():
+    test_readme = File("README.md", rfp(
+        "test_directories", "examples_article", "DataAnalysis",
+        "2020_climate-model-predict", "2020-02-08_prediction-errors", "README.md"))
+
+    converter = MarkdownFileConverter({
+        "match": "(.*)"
+    }, "TestMarkdownFileConverter")
+
+    m = converter.match(File("test_tool.py", rfp(
+        "test_tool.py")))
+    assert m is None
+
+    m = converter.match(test_readme)
+    assert m is not None
+    assert m.__class__ == dict
+    assert len(m) == 0
+
+    converter = MarkdownFileConverter({
+        "match": "README.md"
+    }, "TestMarkdownFileConverter")
+
+    m = converter.match(test_readme)
+    assert m is not None
+    assert len(m) == 0
+
+    children = converter.create_children(None, test_readme)
+    assert len(children) == 5
+    assert children[1].__class__ == DictTextElement
+    assert children[1].name == "description"
+    assert children[1].value.__class__ == str
+
+    assert children[0].__class__ == DictTextElement
+    assert children[0].name == "responsible"
+    assert children[0].value.__class__ == str
+
+    test_readme2 = File("README.md", rfp("test_directories", "examples_article",
+                        "ExperimentalData", "2020_SpeedOfLight", "2020-01-01_TimeOfFlight", "README.md"))
+
+    m = converter.match(test_readme2)
+    assert m is not None
+    assert len(m) == 0
+
+    children = converter.create_children(None, test_readme2)
+    assert len(children) == 2
+    assert children[1].__class__ == DictTextElement
+    assert children[1].name == "description"
+    assert children[1].value.__class__ == str
+
+    assert children[0].__class__ == DictListElement
+    assert children[0].name == "responsible"
+    assert children[0].value.__class__ == list
diff --git a/unittests/test_identifiable_adapters.py b/unittests/test_identifiable_adapters.py
new file mode 100644
index 0000000000000000000000000000000000000000..9730461020c6c582188db58df6524246c0a1042c
--- /dev/null
+++ b/unittests/test_identifiable_adapters.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+# encoding: utf-8
+#
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2021 Indiscale GmbH <info@indiscale.com>
+# Copyright (C) 2021 Henrik tom Wörden <h.tomwoerden@indiscale.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# ** end header
+#
+
+"""
+test identifiable_adapters module
+"""
+
+from datetime import datetime
+from newcrawler.identifiable_adapters import IdentifiableAdapter
+import caosdb as db
+
+
+def test_create_query_for_identifiable():
+    query = IdentifiableAdapter.create_query_for_identifiable(
+        db.Record().add_parent("Person")
+        .add_property("first_name", value="A")
+        .add_property("last_name", value="B"))
+    assert query.lower() == "find record person with 'first_name'='a' and 'last_name'='b' "
+
+    query = IdentifiableAdapter.create_query_for_identifiable(
+        db.Record(name="A").add_parent("B")
+        .add_property("c", value="c")
+        .add_property("d", value=5)
+        .add_property("e", value=5.5)
+        .add_property("f", value=datetime(2020, 10, 10))
+        .add_property("g", value=True)
+        .add_property("h", value=db.Record(id=1111))
+        .add_property("i", value=db.File(id=1112))
+        .add_property("j", value=[2222, db.Record(id=3333)]))
+    assert (query.lower() == "find record b with name='a' and 'c'='c' and 'd'='5' and 'e'='5.5'"
+            " and 'f'='2020-10-10t00:00:00' and 'g'='true' and 'h'='1111' and 'i'='1112' and "
+            "'j'='2222' and 'j'='3333' ")
diff --git a/unittests/test_tool.py b/unittests/test_tool.py
index 23912ff133fdb7dceb1805907d24a57578fc63ee..4b55de8749d3651e4e21482bd903c7da62a96d58 100755
--- a/unittests/test_tool.py
+++ b/unittests/test_tool.py
@@ -4,7 +4,6 @@
 # A. Schlemmer, 06/2021
 
 from newcrawler import Crawler
-from newcrawler.converters import MarkdownFileConverter
 from newcrawler.structure_elements import File, DictTextElement, DictListElement
 from newcrawler.identifiable_adapters import IdentifiableAdapter, LocalStorageIdentifiableAdapter
 from functools import partial
@@ -69,7 +68,8 @@ def ident(crawler):
         .add_property(name="identifier"))
     return ident
 
-def test_crawler(crawler):
+
+def test_record_structure_generation(crawler):
     subd = crawler.debug_tree[dircheckstr("DataAnalysis")]
     subc = crawler.debug_metadata["copied"][dircheckstr("DataAnalysis")]
     assert len(subd) == 2
@@ -138,59 +138,6 @@ def test_crawler(crawler):
     assert subc[0]["identifier"] is False
 
 
-def test_markdown_converter():
-    test_readme = File("README.md", rfp(
-        "test_directories", "examples_article", "DataAnalysis",
-        "2020_climate-model-predict", "2020-02-08_prediction-errors", "README.md"))
-
-    converter = MarkdownFileConverter({
-        "match": "(.*)"
-    }, "TestMarkdownFileConverter")
-
-    m = converter.match(File("test_tool.py", rfp(
-        "test_tool.py")))
-    assert m is None
-
-    m = converter.match(test_readme)
-    assert m is not None
-    assert m.__class__ == dict
-    assert len(m) == 0
-
-    converter = MarkdownFileConverter({
-        "match": "README.md"
-    }, "TestMarkdownFileConverter")
-
-    m = converter.match(test_readme)
-    assert m is not None
-    assert len(m) == 0
-
-    children = converter.create_children(None, test_readme)
-    assert len(children) == 5
-    assert children[1].__class__ == DictTextElement
-    assert children[1].name == "description"
-    assert children[1].value.__class__ == str
-
-    assert children[0].__class__ == DictTextElement
-    assert children[0].name == "responsible"
-    assert children[0].value.__class__ == str
-
-    test_readme2 = File("README.md", rfp("test_directories", "examples_article",
-                        "ExperimentalData", "2020_SpeedOfLight", "2020-01-01_TimeOfFlight", "README.md"))
-
-    m = converter.match(test_readme2)
-    assert m is not None
-    assert len(m) == 0
-
-    children = converter.create_children(None, test_readme2)
-    assert len(children) == 2
-    assert children[1].__class__ == DictTextElement
-    assert children[1].name == "description"
-    assert children[1].value.__class__ == str
-
-    assert children[0].__class__ == DictListElement
-    assert children[0].name == "responsible"
-    assert children[0].value.__class__ == list
-
 # def prepare_test_record_file():
 #     ident = LocalStorageIdentifiableAdapter()
 #     crawler = Crawler(debug=True, identifiableAdapter=ident)
@@ -217,8 +164,8 @@ def test_ambigious_records(crawler, ident):
 
 def test_crawler_update_list(crawler, ident):
     crawler.copy_attributes = Mock()
-    # If the following assertions fail, that is a hint, that the test file records.xml is
-    # incorrect:
+    # If the following assertions fail, that is a hint, that the test file records.xml has changed
+    # and this needs to be updated:
     assert len(ident.get_records()) == 18
     assert len([r for r in ident.get_records() if r.parents[0].name == "Person"]) == 5
     assert len([r for r in ident.get_records() if r.parents[0].name == "Measurement"]) == 11
@@ -295,37 +242,41 @@ def test_crawler_update_list(crawler, ident):
     assert len(updl) == 0
 
 
-def test_identifiable_update(crawler, ident):
-    # change one value in updateList and then run the synchronization:
-    meas = [r for r in crawler.updateList if r.parents[0].name == "Measurement"][0]
-    meas.get_property("responsible").value = []
-    insl, updl = crawler.synchronize()
-    assert len(updl) == 1
-
-
-def test_identifiable_update2(crawler, ident):
-    # change one unit in updateList and then run the synchronization:
-    meas = [r for r in crawler.updateList if r.parents[0].name == "Measurement"][0]
-    meas.get_property("description").unit = "cm"
-    insl, updl = crawler.synchronize()
-    assert len(updl) == 1
-
-
-def test_identifiable_update3(crawler, ident):
-    # change values of multiple records in updateList and then run the synchronization:
-    meas = [r for r in crawler.updateList if r.parents[0].name == "Measurement"]
-    meas[0].get_property("responsible").value = []
-    meas[3].get_property("responsible").value = []
-    insl, updl = crawler.synchronize()
-    assert len(updl) == 2
-
-
-def test_identifiable_adapter():
-    query = IdentifiableAdapter.create_query_for_identifiable(
-        db.Record().add_parent("Person")
-        .add_property("first_name", value="A")
-        .add_property("last_name", value="B"))
-    assert query.lower() == "find record person with 'first_name'='a' and 'last_name'='b' "
+def test_remove_unnecessary_updates():
+    # test trvial case
+    upl = [db.Record().add_parent("A")]
+    irs = [db.Record().add_parent("A")]
+    Crawler.remove_unnecessary_updates(upl, irs)
+    assert len(upl) == 0
+
+    # test property difference case
+    # TODO this should work right?
+    #upl = [db.Record().add_parent("A").add_property("a", 3)]
+    # irs = [db.Record().add_parent("A")]  # ID should be s
+    #Crawler.remove_unnecessary_updates(upl, irs)
+    #assert len(upl) == 1
+
+    # test value difference case
+    upl = [db.Record().add_parent("A").add_property("a", 5)]
+    irs = [db.Record().add_parent("A").add_property("a")]
+    Crawler.remove_unnecessary_updates(upl, irs)
+    assert len(upl) == 1
+    upl = [db.Record().add_parent("A").add_property("a", 5)]
+    irs = [db.Record().add_parent("A").add_property("a", 5)]
+    Crawler.remove_unnecessary_updates(upl, irs)
+    assert len(upl) == 0
+
+    # test unit difference case
+    upl = [db.Record().add_parent("A").add_property("a", unit='cm')]
+    irs = [db.Record().add_parent("A").add_property("a")]
+    Crawler.remove_unnecessary_updates(upl, irs)
+    assert len(upl) == 1
+
+    # test None difference case
+    upl = [db.Record().add_parent("A").add_property("a")]
+    irs = [db.Record().add_parent("A").add_property("a", 5)]
+    Crawler.remove_unnecessary_updates(upl, irs)
+    assert len(upl) == 1
 
 
 @pytest.mark.xfail