diff --git a/.gitignore b/.gitignore
index 0cf92ee328c382ed5e225d8a9c689e150611266b..4402aa11bc399c03400c4427c669b93ebb2637ce 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,4 @@ provenance.yml
 src/doc/_apidoc/
 start_caosdb_docker.sh
 src/doc/_apidoc
+/dist/
diff --git a/integrationtests/test_issues.py b/integrationtests/test_issues.py
new file mode 100644
index 0000000000000000000000000000000000000000..f0f2a7876f9db85aeff768c3659915ef2b0cb9f5
--- /dev/null
+++ b/integrationtests/test_issues.py
@@ -0,0 +1,127 @@
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2022 Indiscale GmbH <info@indiscale.com>
+#               2022 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/>.
+#
+from pytest import fixture, mark
+
+import caosdb as db
+
+from caoscrawler.crawl import Crawler
+from caoscrawler.identifiable_adapters import CaosDBIdentifiableAdapter
+from caoscrawler.structure_elements import Dict
+
+
+@fixture
+def clear_database():
+    db.execute_query("FIND ENTITY").delete(raise_exception_on_error=False)
+
+
+@mark.xfail(reason="See issue https://gitlab.com/caosdb/caosdb-crawler/-/issues/23")
+def test_issue_23(clear_database):
+    """Test that an update leaves existing properties, that were not found by
+    the crawler, unchanged.
+
+    """
+
+    # insert a simplistic model an arecord of type TestType with identifying
+    # property and prop_a, but not prop_b.
+    prop_ident = db.Property(name="identifying_prop", datatype=db.TEXT)
+    prop_a = db.Property(name="prop_a", datatype=db.TEXT)
+    prop_b = db.Property(name="prop_b", datatype=db.TEXT)
+    rt = db.RecordType(name="TestType")
+    rec = db.Record(name="TestRec").add_parent(rt)
+    rec.add_property(name="identifying_prop", value="identifier")
+    rec.add_property(name="prop_a", value="something")
+    db.Container().extend([prop_ident, prop_a, prop_b, rt, rec]).insert()
+
+    # set up crawler, first cfood defining a TestType record with
+    # identifying_prop and prop_b, but not prop_a ...
+    crawler_definition = {
+        "DictTest": {
+            "type": "Dict",
+            "match": "(.*)",
+            "records": {
+                "TestType": {}
+            },
+            "subtree": {
+                "identifying_element": {
+                    "type": "DictTextElement",
+                    "match_name": "ident",
+                    "match_value": "(?P<ident_value>.*)",
+                    "records": {
+                        "TestType": {
+                            "identifying_prop": "$ident_value"
+                        }
+                    }
+                },
+                "other_element": {
+                    "type": "DictTextElement",
+                    "match_name": "prop_b",
+                    "match_value": "(?P<other_value>.*)",
+                    "records": {
+                        "TestType": {
+                            "prop_b": "$other_value"
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    # register identifiable for TestType
+    ident = CaosDBIdentifiableAdapter()
+    ident.register_identifiable("TestType", db.RecordType().add_parent(
+        name="TestType").add_property(name="identifying_prop"))
+
+    crawler = Crawler(debug=True, identifiableAdapter=ident)
+    converter_registry = crawler.load_converters(crawler_definition)
+
+    # the dictionary to be crawled...
+    test_dict = {
+        "ident": "identifier",
+        "prop_b": "something_else"
+    }
+
+    records = crawler.start_crawling(
+        Dict("TestDict", test_dict), crawler_definition, converter_registry)
+
+    assert len(records) == 1
+    rec_crawled = records[0]
+    assert rec_crawled.parents[0].name == "TestType"
+    assert rec_crawled.get_property("identifying_prop") is not None
+    assert rec_crawled.get_property("identifying_prop").value == "identifier"
+    assert rec_crawled.get_property("prop_b") is not None
+    assert rec_crawled.get_property("prop_b").value == "something_else"
+    # no interaction with the database yet, so the rrecord shouldn't have a prop_a yet
+    assert rec_crawled.get_property("prop_a") is None
+
+    # synchronize with database and update the record
+    ins, ups = crawler.synchronize()
+    assert len(ins) == 0
+    assert len(ups) == 1
+
+    # retrieve and check that name and properties have been combined correctly
+    rec_retrieved = db.Record(id=rec.id).retrieve()
+    assert rec_retrieved.name == rec.name
+    assert rec_retrieved.get_property(
+        "identifying_prop").value == rec.get_property("identifying_prop").value
+    assert rec_retrieved.get_property(
+        "prop_a").value == rec.get_property("prop_a").value
+    assert rec_retrieved.get_property(
+        "identifying_prop").value == rec_crawled.get_property("identifying_prop").value
+    assert rec_retrieved.get_property(
+        "prop_b").value == rec_crawled.get_property("prop_b").value