diff --git a/Framework/DataHandling/test/CMakeLists.txt b/Framework/DataHandling/test/CMakeLists.txt
index 069949d50b8b0ccdb96e47bb1d528ba4040d2077..75f98634a22221d34ffad2469c2c323e8ec7fb05 100644
--- a/Framework/DataHandling/test/CMakeLists.txt
+++ b/Framework/DataHandling/test/CMakeLists.txt
@@ -17,7 +17,8 @@ if(CXXTEST_FOUND)
       ../../TestHelpers/src/NexusTestHelper.cpp
       ../../TestHelpers/src/ONCatHelper.cpp
       ../../TestHelpers/src/ParallelRunner.cpp
-      NXcanSASTestHelper.cpp)
+      NXcanSASTestHelper.cpp
+	  ../../TestHelpers/src/FileResource.cpp)
 
   cxxtest_add_test(DataHandlingTest ${TEST_FILES})
   target_include_directories(DataHandlingTest SYSTEM
diff --git a/Framework/DataHandling/test/LoadNexusProcessed2Test.h b/Framework/DataHandling/test/LoadNexusProcessed2Test.h
index a5c0ebaac09d013eb4f6e37e214322f0b85a18cc..8321b8e653bb388336ec7bc244c61e86891842a2 100644
--- a/Framework/DataHandling/test/LoadNexusProcessed2Test.h
+++ b/Framework/DataHandling/test/LoadNexusProcessed2Test.h
@@ -100,7 +100,7 @@ public:
 
     using namespace Mantid::HistogramData;
 
-    ScopedFileHandle fileInfo("test_ess_instrument.nxs");
+    FileResource fileInfo("test_ess_instrument.nxs");
 
     auto wsIn =
         test_utility::make_workspace("V20_4-tubes_90deg_Definition_v01.xml");
@@ -123,7 +123,7 @@ public:
   void test_reading_mappings() {
     using Mantid::SpectrumDefinition;
     using namespace Mantid::Indexing;
-    ScopedFileHandle fileInfo("test_no_spectra_mapping.nxs");
+    FileResource fileInfo("test_no_spectra_mapping.nxs");
     auto wsIn =
         WorkspaceCreationHelper::create2DWorkspaceWithRectangularInstrument(
             2 /*numBanks*/, 10 /*numPixels*/, 12 /*numBins*/);
@@ -172,7 +172,7 @@ public:
 
   void test_demonstrate_old_loader_incompatible() {
 
-    ScopedFileHandle fileInfo("test_demo_file_for_incompatible.nxs");
+    FileResource fileInfo("test_demo_file_for_incompatible.nxs");
 
     auto wsIn =
         test_utility::make_workspace("V20_4-tubes_90deg_Definition_v01.xml");
diff --git a/Framework/DataHandling/test/SaveNexusESSTest.h b/Framework/DataHandling/test/SaveNexusESSTest.h
index a9421a9831ecda5524eb79e8b40c4234d120bdb4..b744739f9d1d38d35990ecc6e74170cf16fa3456 100644
--- a/Framework/DataHandling/test/SaveNexusESSTest.h
+++ b/Framework/DataHandling/test/SaveNexusESSTest.h
@@ -97,7 +97,7 @@ public:
 
   void test_exec_rectangular_instrument_details() {
     using namespace Mantid::NexusGeometry;
-    ScopedFileHandle fileInfo("test_rectangular_instrument.nxs");
+    FileResource fileInfo("test_rectangular_instrument.nxs");
     auto ws =
         WorkspaceCreationHelper::create2DWorkspaceWithRectangularInstrument(
             1 /*numBanks*/, 10 /*numPixels*/, 10 /*numBins*/);
@@ -124,7 +124,7 @@ public:
   }
 
   void test_exec_rectangular_data() {
-    ScopedFileHandle fileInfo("test_rectangular_data.nxs");
+    FileResource fileInfo("test_rectangular_data.nxs");
     auto wsIn =
         WorkspaceCreationHelper::create2DWorkspaceWithRectangularInstrument(
             1 /*numBanks*/, 10 /*numPixels*/, 12 /*numBins*/);
@@ -142,7 +142,7 @@ public:
 
     using namespace Mantid::HistogramData;
 
-    ScopedFileHandle fileInfo("test_ess_instrument.nxs");
+    FileResource fileInfo("test_ess_instrument.nxs");
 
     auto wsIn = test_utility::from_instrument_file(
         "V20_4-tubes_90deg_Definition_v01.xml");
@@ -165,7 +165,7 @@ public:
   void test_demonstrate_spectra_detector_map_saved() {
 
     using namespace Mantid::Indexing;
-    ScopedFileHandle fileInfo("test_no_spectra_mapping.nxs");
+    FileResource fileInfo("test_no_spectra_mapping.nxs");
     auto wsIn =
         WorkspaceCreationHelper::create2DWorkspaceWithRectangularInstrument(
             2 /*numBanks*/, 10 /*numPixels*/, 12 /*numBins*/);
@@ -211,7 +211,7 @@ public:
     // This is testing the core routine, but we put it here and not in
     // NexusGeometrySave because we need access to WorkspaceCreationHelpers for
     // this.
-    ScopedFileHandle fileResource("test_with_full_workspace.hdf5");
+    FileResource fileResource("test_with_full_workspace.hdf5");
     std::string destinationFile = fileResource.fullPath();
     // auto ws = WorkspaceCreationHelper::create2DWorkspaceWithFullInstrument(
     //    10 /*histograms*/, 100 /*bins*/);
@@ -225,8 +225,7 @@ public:
   }
 
   void test_regression_iris() {
-    ScopedFileHandle handle(
-        "test_regression_iris.nxs"); // IRIS has single monitors
+    FileResource handle("test_regression_iris.nxs"); // IRIS has single monitors
     auto iris = test_utility::from_instrument_file2("IRIS");
     do_execute(handle.fullPath(), iris);
     auto iris_reloaded = test_utility::reload(handle.fullPath());
diff --git a/Framework/DataHandling/test/SaveNexusGeometryTest.h b/Framework/DataHandling/test/SaveNexusGeometryTest.h
index c9dd2a4203b96eaaa460d22021df227f644c8a8b..039b92e5d2d77927cfa6dcafa41be60d6484c769 100644
--- a/Framework/DataHandling/test/SaveNexusGeometryTest.h
+++ b/Framework/DataHandling/test/SaveNexusGeometryTest.h
@@ -39,7 +39,7 @@ public:
 
   void test_exec() {
 
-    ScopedFileHandle fileResource("algorithm_test_file.hdf5");
+    FileResource fileResource("algorithm_test_file.hdf5");
     auto destinationFile = fileResource.fullPath();
     // Create test input if necessary
     Mantid::API::IEventWorkspace_sptr inputWS =
@@ -67,7 +67,7 @@ public:
   void
   test_execution_succesful_when_no_h5_root_provided_and_default_root_is_used() {
 
-    ScopedFileHandle fileResource("algorithm_no_h5_root_file.hdf5");
+    FileResource fileResource("algorithm_no_h5_root_file.hdf5");
     auto destinationFile = fileResource.fullPath();
     // Create test input if necessary
     Mantid::API::IEventWorkspace_sptr inputWS =
@@ -97,7 +97,7 @@ public:
     into the Input workspace property.
     */
 
-    ScopedFileHandle fileResource(
+    FileResource fileResource(
         "algorithm_no_instrument_in_workspace_provided_test_file.hdf5");
     auto destinationFile = fileResource.fullPath();
     // Create test input if necessary
@@ -134,7 +134,7 @@ public:
     when invalid file extension is passed into fileName property.
     */
 
-    ScopedFileHandle fileResource(
+    FileResource fileResource(
         "algorithm_invalid_extension_provided_test_file.txt");
     auto destinationFile = fileResource.fullPath();
     // Create test workspace
@@ -163,7 +163,7 @@ public:
 
   void test_eight_pack() {
 
-    ScopedFileHandle fileResource("eight_pack.hdf5");
+    FileResource fileResource("eight_pack.hdf5");
     auto destinationFile = fileResource.fullPath();
 
     Mantid::DataHandling::LoadEmptyInstrument alg;
@@ -193,7 +193,7 @@ public:
     exception.
     */
 
-    ScopedFileHandle fileResource("duplicate_names_test.hdf5");
+    FileResource fileResource("duplicate_names_test.hdf5");
     auto destinationFile = fileResource.fullPath();
 
     Mantid::DataHandling::LoadEmptyInstrument loader;
diff --git a/Framework/NexusGeometry/src/NexusGeometrySave.cpp b/Framework/NexusGeometry/src/NexusGeometrySave.cpp
index cb4962928ee96a69741b04a2b3b3e9ddf0152b67..a1e266d2f94c7282e6ef3746e5c29ef572e8b3c8 100644
--- a/Framework/NexusGeometry/src/NexusGeometrySave.cpp
+++ b/Framework/NexusGeometry/src/NexusGeometrySave.cpp
@@ -5,7 +5,8 @@
 //     & Institut Laue - Langevin
 // SPDX - License - Identifier: GPL - 3.0 +
 
-/** NexusGeometrySave::saveInstrument :
+/*
+ * NexusGeometrySave::saveInstrument :
  * Save methods to save geometry and metadata from memory
  * to disk in Nexus file format for Instrument 2.0.
  *
@@ -77,8 +78,8 @@ inline H5::Group tryCreateGroup(const H5::Group &parentGroup,
   }
   return parentGroup.createGroup(childGroupName);
 }
-
-/** Function toStdVector (Overloaded). Store data in Mantid::Kernel::V3D vector
+/*
+ * Function toStdVector (Overloaded). Store data in Mantid::Kernel::V3D vector
  * into std::vector<double> vector. Used by saveInstrument to write array-type
  * datasets to file.
  *
@@ -95,7 +96,8 @@ std::vector<double> toStdVector(const V3D &data) {
   return stdVector;
 }
 
-/** Function toStdVector (Overloaded). Store data in Eigen::Vector3d vector
+/*
+ * Function toStdVector (Overloaded). Store data in Eigen::Vector3d vector
  * into std::vector<double> vector. Used by saveInstrument to write array-type
  * datasets to file.
  *
@@ -107,7 +109,8 @@ std::vector<double> toStdVector(const Eigen::Vector3d &data) {
   return toStdVector(Kernel::toV3D(data));
 }
 
-/** Function: isApproxZero. returns true if all values in an variable-sized
+/*
+ * Function: isApproxZero. returns true if all values in an variable-sized
  * std-vector container evaluate to zero with a given level of precision. Used
  * by SaveInstrument methods to determine whether or not to write a dataset to
  * file.
@@ -134,7 +137,8 @@ bool isApproxZero(const Eigen::Quaterniond &data, const double &precision) {
   return data.isApprox(Eigen::Quaterniond(1, 0, 0, 0), precision);
 }
 
-/** Function: strTypeOfSize
+/*
+ * Function: strTypeOfSize
  * Produces the HDF StrType of size equal to that of the
  * input string.
  *
@@ -146,7 +150,8 @@ H5::StrType strTypeOfSize(const std::string &str) {
   return stringType;
 }
 
-/** Function: writeStrDataset
+/*
+ * Function: writeStrDataset
  * writes a StrType HDF dataset and dataset value to a HDF group.
  *
  * @param grp : HDF group object.
@@ -164,7 +169,8 @@ void writeStrDataset(H5::Group &grp, const std::string &dSetName,
   }
 }
 
-/** Function: writeStrAttribute
+/*
+ * Function: writeStrAttribute
  * writes a StrType HDF attribute and attribute value to a HDF group.
  *
  * @param grp : HDF group object.
@@ -182,7 +188,8 @@ void writeStrAttribute(H5::Group &grp, const std::string &attrName,
   }
 }
 
-/** Function: writeStrAttribute
+/*
+ * Function: writeStrAttribute
  * Overload function which writes a StrType HDF attribute and attribute value
  * to a HDF dataset.
  *
@@ -200,19 +207,11 @@ void writeStrAttribute(H5::DataSet &dSet, const std::string &attrName,
   }
 }
 
-// function to create a simple sub-group that has a nexus class attribute,
-// inside a parent group.
-inline H5::Group simpleNXSubGroup(H5::Group &parent, const std::string &name,
-                                  const std::string &nexusAttribute) {
-  H5::Group subGroup = tryCreateGroup(parent, name);
-  writeStrAttribute(subGroup, NX_CLASS, nexusAttribute);
-  return subGroup;
-}
-
-/** Function: writeXYZPixeloffset
+/*
+ * Function: writeXYZPixeloffset
  * write the x, y, and z offset of the pixels from the parent detector bank as
- * HDF5 datasets to HDF5 group. If all of the pixel offsets in either x, y, or
- * z are approximately zero, skips writing that dataset to file.
+ * HDF5 datasets to HDF5 group. If all of the pixel offsets in either x, y, or z
+ * are approximately zero, skips writing that dataset to file.
  * @param grp : HDF5 parent group
  * @param compInfo : Component Info Instrument cache
  * @param idx : index of bank in cache.
@@ -342,7 +341,8 @@ void writeSpectra(H5::Group &grp, const SpectraMappings &mappings) {
   write1DIntDataset(grp, SPECTRA_NUMBERS, mappings.spectra_ids);
 }
 
-/** Function: writeNXMonitorNumber
+/*
+ * Function: writeNXMonitorNumber
  * For use with NXmonitor group. write 'detector_id's of an NXmonitor, which
  * is a specific type of pixel, to its group.
  *
@@ -378,13 +378,13 @@ void writeNXMonitorNumber(H5::Group &grp, const int monitorID) {
   }
 }
 
-/** Function: writeLocation
+/*
+ * Function: writeLocation
  * For use with NXdetector group. Writes absolute position of detector bank to
  * dataset and metadata as attributes.
  *
  * @param grp : NXdetector group : (HDF group)
- * @param position : Eigen::Vector3d position of component in instrument
- * cache.
+ * @param position : Eigen::Vector3d position of component in instrument cache.
  */
 inline void writeLocation(H5::Group &grp, const Eigen::Vector3d &position) {
 
@@ -445,7 +445,8 @@ inline void writeLocation(H5::Group &grp, const Eigen::Vector3d &position) {
   dependsOn.write(strSize, dependency);
 }
 
-/** Function: writeOrientation
+/*
+ * Function: writeOrientation
  * For use with NXdetector group. Writes the absolute rotation of detector
  * bank to dataset and metadata as attributes.
  *
@@ -649,38 +650,6 @@ class NexusGeometrySaveImpl {
 public:
   enum class Mode { Trunc, Append };
 
-private:
-  const Mode m_mode;
-
-  H5::Group openOrCreateGroup(const H5::Group &parent, const std::string &name,
-                              const std::string &classType) {
-
-    if (m_mode == Mode::Append) {
-      // Find by class and by name
-      auto results = utilities::findGroups(parent, classType);
-      for (auto &result : results) {
-        auto resultName = H5_OBJ_NAME(result);
-        // resultName gives full path. We match the last name on the path
-        if (std::regex_match(resultName, std::regex(".*/" + name + "$"))) {
-          return result;
-        }
-      }
-    }
-    // We can't find it, or we are writing from scratch anyway. We need to
-    // verify no-duplicates
-    return tryCreateGroup(parent, name);
-  }
-
-  // function to create a simple sub-group that has a nexus class attribute,
-  // inside a parent group.
-  H5::Group simpleNXSubGroup(H5::Group &parent, const std::string &name,
-                             const std::string &nexusAttribute) {
-    H5::Group subGroup = openOrCreateGroup(parent, name, nexusAttribute);
-    writeStrAttribute(subGroup, NX_CLASS, nexusAttribute);
-    return subGroup;
-  }
-
-public:
   explicit NexusGeometrySaveImpl(Mode mode) : m_mode(mode) {}
   NexusGeometrySaveImpl(const NexusGeometrySaveImpl &) =
       delete; // No intention to suport copies
@@ -1010,7 +979,37 @@ public:
     writeDetectorIndex(childGroup, mappings);
     writeSpectra(childGroup, mappings);
   }
-}; // namespace
+
+private:
+  const Mode m_mode;
+
+  H5::Group openOrCreateGroup(const H5::Group &parent, const std::string &name,
+                              const std::string &classType) {
+
+    if (m_mode == Mode::Append) {
+      // Find by class and by name
+      auto results = utilities::findGroups(parent, classType);
+      for (auto &result : results) {
+        auto resultName = H5_OBJ_NAME(result);
+        // resultName gives full path. We match the last name on the path
+        if (std::regex_match(resultName, std::regex(".*/" + name + "$"))) {
+          return result;
+        }
+      }
+    }
+    // We can't find it, or we are writing from scratch anyway
+    return tryCreateGroup(parent, name);
+  }
+
+  // function to create a simple sub-group that has a nexus class attribute,
+  // inside a parent group.
+  H5::Group simpleNXSubGroup(H5::Group &parent, const std::string &name,
+                             const std::string &nexusAttribute) {
+    H5::Group subGroup = openOrCreateGroup(parent, name, nexusAttribute);
+    writeStrAttribute(subGroup, NX_CLASS, nexusAttribute);
+    return subGroup;
+  }
+}; // class NexusGeometrySaveImpl
 
 /*
  * Function: saveInstrument
@@ -1064,8 +1063,7 @@ void saveInstrument(const Geometry::ComponentInfo &compInfo,
   // Looping from highest to lowest component index is critical
   for (size_t index = compInfo.root() - 1; index >= detInfo.size(); --index) {
     if (Geometry::ComponentInfoBankHelpers::isSaveableBank(compInfo, index)) {
-      const bool needed = isDesiredNXDetector(index, saved_indices, compInfo);
-      if (needed) {
+      if (isDesiredNXDetector(index, saved_indices, compInfo)) {
         if (reporter != nullptr)
           reporter->report();
         writer.detector(instrument, compInfo, detIds, index);
@@ -1088,10 +1086,11 @@ void saveInstrument(const Geometry::ComponentInfo &compInfo,
 
 } // saveInstrument
 
-/*
+/**
  * Function: saveInstrument (overload)
- * calls the save methods to write components to file after exception checking.
- * Produces a Nexus format file containing the Instrument geometry and metadata.
+ * calls the save methods to write components to file after exception
+ * checking. Produces a Nexus format file containing the Instrument geometry
+ * and metadata.
  *
  * @param instrPair : instrument 2.0  object.
  * @param fullPath : save destination as full path.
@@ -1153,6 +1152,7 @@ void saveInstrument(const Mantid::API::MatrixWorkspace &ws,
   // save NXdetectors
   auto detToIndexMap =
       ws.getDetectorIDToWorkspaceIndexMap(true /*throw if multiples*/);
+  // save NXdetectors
   std::list<size_t> saved_indices;
   // Looping from highest to lowest component index is critical
   for (size_t index = compInfo.root() - 1; index >= detInfo.size(); --index) {
@@ -1163,6 +1163,7 @@ void saveInstrument(const Mantid::API::MatrixWorkspace &ws,
         SpectraMappings mappings =
             makeMappings(compInfo, detToIndexMap, ws.indexInfo(),
                          ws.spectrumInfo(), detIds, index);
+
         if (reporter != nullptr)
           reporter->report();
         writer.detector(instrument, compInfo, detIds, index, mappings);
diff --git a/Framework/NexusGeometry/test/CMakeLists.txt b/Framework/NexusGeometry/test/CMakeLists.txt
index 771686ced99382f0837c0f81b168868175ea3c4d..573d6ccc0d9b8e03557af290fe5386f076466fa6 100644
--- a/Framework/NexusGeometry/test/CMakeLists.txt
+++ b/Framework/NexusGeometry/test/CMakeLists.txt
@@ -8,7 +8,8 @@ if(CXXTEST_FOUND)
 
   set(TESTHELPER_SRCS ../../TestHelpers/src/NexusGeometryTestHelpers.cpp
                       ../../TestHelpers/src/JSONGeometryParserTestHelper.cpp
-                      ../../TestHelpers/src/ComponentCreationHelper.cpp)
+                      ../../TestHelpers/src/ComponentCreationHelper.cpp
+					  ../../TestHelpers/src/FileResource.cpp)
 
   cxxtest_add_test(NexusGeometryTest ${TEST_FILES})
 
diff --git a/Framework/NexusGeometry/test/NexusGeometrySaveTest.h b/Framework/NexusGeometry/test/NexusGeometrySaveTest.h
index 4ef7c4963529e15b175a46377cdd8c397fd0a995..17d5d26b6c459ac60f957905066b06cc0de932f8 100644
--- a/Framework/NexusGeometry/test/NexusGeometrySaveTest.h
+++ b/Framework/NexusGeometry/test/NexusGeometrySaveTest.h
@@ -56,7 +56,7 @@ used.
 
   void test_providing_invalid_path_throws() {
 
-    ScopedFileHandle fileResource("invalid_path_to_file_test_file.hdf5");
+    FileResource fileResource("invalid_path_to_file_test_file.hdf5");
     const std::string badDestinationPath =
         "false_directory\\" + fileResource.fullPath();
 
@@ -77,7 +77,7 @@ used.
     EXPECT_CALL(progressRep, doReport(testing::_))
         .Times(nbanks); // Progress report once for each bank
 
-    ScopedFileHandle fileResource("progress_report_test_file.hdf5");
+    FileResource fileResource("progress_report_test_file.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
     auto instrument = ComponentCreationHelper::createTestInstrumentRectangular2(
@@ -92,7 +92,7 @@ used.
 
   void test_false_file_extension_throws() {
 
-    ScopedFileHandle fileResource("invalid_extension_test_file.abc");
+    FileResource fileResource("invalid_extension_test_file.abc");
     std::string destinationFile = fileResource.fullPath();
 
     auto instrument = ComponentCreationHelper::createMinimalInstrument(
@@ -120,7 +120,7 @@ used.
             true, false, true);
     auto instr = Mantid::Geometry::InstrumentVisitor::makeWrappers(*instrument);
 
-    ScopedFileHandle fileResource("check_no_sample_throws_test_file.hdf5");
+    FileResource fileResource("check_no_sample_throws_test_file.hdf5");
     auto destinationFile = fileResource.fullPath();
 
     // instrument cache
@@ -155,7 +155,7 @@ used.
     // instrument cache
     auto &compInfo = (*instr.first);
 
-    ScopedFileHandle fileResource("check_no_source_throws_test_file.hdf5");
+    FileResource fileResource("check_no_source_throws_test_file.hdf5");
     auto destinationFile = fileResource.fullPath();
 
     TS_ASSERT(compInfo.hasDetectorInfo()); // rule out throw by no detector info
@@ -182,7 +182,7 @@ used.
         V3D(0, 0, -10), V3D(0, 0, 2), V3D(0, 0, 10));
     auto instr = Mantid::Geometry::InstrumentVisitor::makeWrappers(*instrument);
 
-    ScopedFileHandle fileResource("check_nxsource_group_test_file.hdf5");
+    FileResource fileResource("check_nxsource_group_test_file.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
     TS_ASSERT_THROWS(NexusGeometrySave::saveInstrument(instr, destinationFile,
@@ -218,7 +218,7 @@ used.
     // has NXclass attribute of NXentry. as required by the Nexus file format.
 
     // RAII file resource for test file destination
-    ScopedFileHandle fileResource("check_nxentry_group_test_file.nxs");
+    FileResource fileResource("check_nxentry_group_test_file.nxs");
     std::string destinationFile = fileResource.fullPath();
 
     // test instrument
@@ -242,7 +242,7 @@ used.
     // data is saved to a group of NXclass NXinstrument
 
     // RAII file resource for test file destination
-    ScopedFileHandle fileResource("check_nxinstrument_group_test_file.hdf5");
+    FileResource fileResource("check_nxinstrument_group_test_file.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
     // test instrument with some geometry
@@ -269,7 +269,7 @@ used.
     // compatibility reasons
 
     // RAII file resource for test file destination
-    ScopedFileHandle fileResource("check_instrument_name_test_file.nxs");
+    FileResource fileResource("check_instrument_name_test_file.nxs");
     auto destinationFile = fileResource.fullPath();
 
     // test instrument
@@ -307,12 +307,34 @@ used.
         NAME, compInfo.name(compInfo.root()), path));
   }
 
+  void
+  test_NXclass_without_name_is_assigned_unique_default_name_for_each_group() {
+    // this test will try to save and unnamed instrument with multiple unnamed
+    // detector banks, to verify that the unique group names which
+    // saveInstrument provides for each NXclass do not throw a H5 error due to
+    // duplication of group names. If any group in the same tree path share the
+    // same name, HDF5 will throw a group exception. In this test, we expect no
+    // such exception to throw.
+
+    // RAII file resource for test file destination.
+    FileResource fileResource("default_group_names_test.hdf5");
+    std::string destinationFile = fileResource.fullPath();
+
+    // unnamed ("") instrument with multiple unnamed detector banks ("")
+    auto instrument = ComponentCreationHelper::createTestUnnamedRectangular2(
+        2 /*number of banks*/, 2);
+    auto instr = Mantid::Geometry::InstrumentVisitor::makeWrappers(*instrument);
+
+    TS_ASSERT_THROWS_NOTHING(NexusGeometrySave::saveInstrument(
+        instr, destinationFile, DEFAULT_ROOT_ENTRY_NAME, m_mockLogger));
+  }
+
   void test_nxsource_group_exists_and_is_in_nxinstrument_group() {
     // this test checks that inside of the NXinstrument group, the the source
     // data is saved to a group of NXclass NXsource
 
     // RAII file resource for test file destination.
-    ScopedFileHandle fileResource("check_nxsource_group_test_file.hdf5");
+    FileResource fileResource("check_nxsource_group_test_file.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
     // test instrument
@@ -336,7 +358,7 @@ used.
 
   void test_nxsample_group_exists_and_is_in_nxentry_group() {
 
-    ScopedFileHandle fileResource("check_nxsource_group_test_file.hdf5");
+    FileResource fileResource("check_nxsource_group_test_file.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
     auto instrument = ComponentCreationHelper::createMinimalInstrument(
@@ -357,7 +379,7 @@ used.
 
   void test_correct_number_of_detectors_saved() {
 
-    ScopedFileHandle fileResource("check_num_of_banks_test.hdf5");
+    FileResource fileResource("check_num_of_banks_test.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
     int banksInInstrument = 3;
@@ -407,7 +429,7 @@ Instrument cache.
    */
 
     // RAII file resource for test file destination
-    ScopedFileHandle fileResource(
+    FileResource fileResource(
         "check_rotation_written_to_nxdetector_test_file.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
@@ -469,7 +491,7 @@ Instrument cache.
     */
 
     // RAII file resource for test file destination
-    ScopedFileHandle fileResource(
+    FileResource fileResource(
         "check_rotation_written_to_nx_monitor_test_file.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
@@ -526,7 +548,7 @@ Instrument cache.
     */
 
     // RAII file resource for test file destination
-    ScopedFileHandle fileResource(
+    FileResource fileResource(
         "check_location_written_to_nxsource_test_file.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
@@ -583,7 +605,7 @@ Instrument cache.
     */
 
     // RAII file resource for test file destination
-    ScopedFileHandle fileResource(
+    FileResource fileResource(
         "check_rotation_written_to_nxsource_test_file.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
@@ -641,7 +663,7 @@ Instrument cache.
     */
 
     // RAII file resource for test file destination
-    ScopedFileHandle fileResource("origin_nx_source_location_file_test.hdf5");
+    FileResource fileResource("origin_nx_source_location_file_test.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
     // prepare geometry for instrument
@@ -690,7 +712,7 @@ Instrument cache.
     const Quat bankRotation(0, V3D(0, 0, 1));  // set (angle) to zero
 
     // RAII file resource for test file destination
-    ScopedFileHandle fileResource("zero_nx_detector_rotation_file_test.hdf5");
+    FileResource fileResource("zero_nx_detector_rotation_file_test.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
     // test instrument with zero source rotation
@@ -729,7 +751,7 @@ Instrument cache.
     */
 
     // RAII file resource for test file destination
-    ScopedFileHandle fileResource("zero_nx_monitor_rotation_file_test.hdf5");
+    FileResource fileResource("zero_nx_monitor_rotation_file_test.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
     V3D someLocation(0.0, 0.0, -5.0); // arbitrary monitor location
@@ -772,7 +794,7 @@ Instrument cache.
     const Quat sourceRotation(0, V3D(0, 0, 1)); // set (angle) to zero
 
     // RAII file resource for test file destination
-    ScopedFileHandle inFileResource("zero_nx_source_rotation_file_test.hdf5");
+    FileResource inFileResource("zero_nx_source_rotation_file_test.hdf5");
     std::string destinationFile = inFileResource.fullPath();
 
     // test instrument with zero rotation
@@ -811,7 +833,7 @@ Instrument cache.
     */
 
     // create RAII file resource for testing
-    ScopedFileHandle fileResource("check_pixel_offset_format_test_file.hdf5");
+    FileResource fileResource("check_pixel_offset_format_test_file.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
     // prepare geometry for instrument
@@ -899,7 +921,7 @@ Instrument cache.
     const V3D sourceLocation(0, 0, 0);           // set to zero
 
     // create RAII file resource for testing
-    ScopedFileHandle fileResource("no_location_dependency_test.hdf5");
+    FileResource fileResource("no_location_dependency_test.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
     // test instrument with location of source at zero
@@ -965,7 +987,7 @@ Instrument cache.
     const Quat sourceRotation(0.0, V3D(0.0, 1.0, 0.0)); // set to zero
 
     // create RAII file resource for testing
-    ScopedFileHandle fileResource("no_orientation_dependency_test.hdf5");
+    FileResource fileResource("no_orientation_dependency_test.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
     // test instrument with rotation of source of zero
@@ -1034,7 +1056,7 @@ Instrument cache.
     const Quat sourceRotation(45, V3D(0, 1, 0)); // arbitrary non-zero
 
     // create RAII file resource for testing
-    ScopedFileHandle fileResource("both_transformations_dependency_test.hdf5");
+    FileResource fileResource("both_transformations_dependency_test.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
     // test instrument with non zero rotation and translation
@@ -1105,8 +1127,7 @@ Instrument cache.
     const Quat sourceRotation(0, V3D(0, 1, 0)); // set to zero
 
     // create RAII file resource for testing
-    ScopedFileHandle fileResource(
-        "neither_transformations_dependency_test.hdf5");
+    FileResource fileResource("neither_transformations_dependency_test.hdf5");
     std::string destinationFile = fileResource.fullPath();
 
     // test instrument with zero translation and rotation
diff --git a/Framework/TestHelpers/inc/MantidTestHelpers/ComponentCreationHelper.h b/Framework/TestHelpers/inc/MantidTestHelpers/ComponentCreationHelper.h
index 04cbc70397cfefa44161ca5d4cfd6435c14ee063..95e7393f4cde9abdf1a20718874401038351648e 100644
--- a/Framework/TestHelpers/inc/MantidTestHelpers/ComponentCreationHelper.h
+++ b/Framework/TestHelpers/inc/MantidTestHelpers/ComponentCreationHelper.h
@@ -216,6 +216,10 @@ Mantid::Geometry::Instrument_sptr
 createTestInstrumentRectangular2(int num_banks, int pixels,
                                  double pixelSpacing = 0.008);
 
+Mantid::Geometry::Instrument_sptr
+createTestUnnamedRectangular2(int num_banks, int pixels,
+                              double pixelSpacing = 0.008);
+
 /// Creates a mimimal valid virtual instrument.
 Mantid::Geometry::Instrument_sptr
 createMinimalInstrument(const Mantid::Kernel::V3D &sourcePos,
diff --git a/Framework/TestHelpers/inc/MantidTestHelpers/FileResource.h b/Framework/TestHelpers/inc/MantidTestHelpers/FileResource.h
index f2939d2adf43b8fed4b8ea9b5e857cbd0acae497..0fa8b39909748b9b8211d15847140bf1d2c64f20 100644
--- a/Framework/TestHelpers/inc/MantidTestHelpers/FileResource.h
+++ b/Framework/TestHelpers/inc/MantidTestHelpers/FileResource.h
@@ -5,8 +5,7 @@
 //     & Institut Laue - Langevin
 // SPDX - License - Identifier: GPL - 3.0 +
 
-/*
- * RAII: Gives a clean file destination and removes the file when
+/** RAII: Gives a clean file destination and removes the file when
  * handle is out of scope. Must be stack allocated.
  *
  * @author Takudzwa Makoni, RAL (UKRI), ISIS
@@ -20,48 +19,17 @@
 #include <iostream>
 #include <string>
 
-class ScopedFileHandle {
+class FileResource {
 
 public:
-  ScopedFileHandle(const std::string &fileName, bool debugMode = false)
-      : m_debugMode(debugMode) {
-
-    const auto temp_dir = boost::filesystem::temp_directory_path();
-    auto temp_full_path = temp_dir;
-    // append full path to temp directory to user input file name
-    temp_full_path /= fileName;
-
-    // Check proposed location and throw std::invalid argument if file does
-    // not exist. otherwise set m_full_path to location.
-
-    if (boost::filesystem::is_directory(temp_dir)) {
-      m_full_path = temp_full_path;
-
-    } else {
-      throw std::invalid_argument("failed to load temp directory: " +
-                                  temp_dir.generic_string());
-    }
-  }
-
-  void setDebugMode(bool mode) { m_debugMode = mode; }
-  std::string fullPath() const { return m_full_path.generic_string(); }
-
-  ~ScopedFileHandle() {
-
-    // file is removed at end of file handle's lifetime
-    if (boost::filesystem::is_regular_file(m_full_path)) {
-      if (!m_debugMode)
-        boost::filesystem::remove(m_full_path);
-      else
-        std::cout << "Debug file at: " << m_full_path << " not removed. "
-                  << std::endl;
-    }
-  }
+  FileResource(const std::string &fileName, bool debugMode = false);
+  void setDebugMode(bool mode);
+  std::string fullPath() const;
+  ~FileResource();
 
 private:
   bool m_debugMode;
   boost::filesystem::path m_full_path; // full path to file
-
   // prevent heap allocation for ScopedFileHandle
 protected:
   static void *operator new(std::size_t); // prevent heap allocation of scalar.
diff --git a/Framework/TestHelpers/src/FileResource.cpp b/Framework/TestHelpers/src/FileResource.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..feddc230d1c8a6077cf2fdbfe09c67b0ff6681bb
--- /dev/null
+++ b/Framework/TestHelpers/src/FileResource.cpp
@@ -0,0 +1,47 @@
+// Mantid Repository : https://github.com/mantidproject/mantid
+//
+// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+//     NScD Oak Ridge National Laboratory, European Spallation Source
+//     & Institut Laue - Langevin
+// SPDX - License - Identifier: GPL - 3.0 +
+
+#include "MantidTestHelpers/FileResource.h"
+#include <boost/filesystem.hpp>
+#include <string>
+
+FileResource::FileResource(const std::string &fileName, bool debugMode)
+    : m_debugMode(debugMode) {
+
+  const auto temp_dir = boost::filesystem::temp_directory_path();
+  auto temp_full_path = temp_dir;
+  // append full path to temp directory to user input file name
+  temp_full_path /= fileName;
+
+  // Check proposed location and throw std::invalid argument if file does
+  // not exist. otherwise set m_full_path to location.
+
+  if (boost::filesystem::is_directory(temp_dir)) {
+    m_full_path = temp_full_path;
+
+  } else {
+    throw std::invalid_argument("failed to load temp directory: " +
+                                temp_dir.generic_string());
+  }
+}
+
+void FileResource::setDebugMode(bool mode) { m_debugMode = mode; }
+std::string FileResource::fullPath() const {
+  return m_full_path.generic_string();
+}
+
+FileResource::~FileResource() {
+
+  // file is removed at end of file handle's lifetime
+  if (boost::filesystem::is_regular_file(m_full_path)) {
+    if (!m_debugMode)
+      boost::filesystem::remove(m_full_path);
+    else
+      std::cout << "Debug file at: " << m_full_path << " not removed. "
+                << std::endl;
+  }
+}