diff --git a/Framework/API/CMakeLists.txt b/Framework/API/CMakeLists.txt
index 5afd011c1e9098f940964d4f6c9d9d7cc0e8d749..3fa7bdf8ff6ad8ad41ae13c977defe2ce4c8d3d3 100644
--- a/Framework/API/CMakeLists.txt
+++ b/Framework/API/CMakeLists.txt
@@ -96,6 +96,7 @@ set ( SRC_FILES
 	src/LogManager.cpp
 	src/LogarithmScale.cpp
 	src/MDGeometry.cpp
+        src/MDFrameValidator.cpp
 	src/MatrixWorkspace.cpp
 	src/MatrixWorkspaceMDIterator.cpp
 	src/ModeratorModel.cpp
@@ -292,6 +293,7 @@ set ( INC_FILES
 	inc/MantidAPI/LogManager.h
 	inc/MantidAPI/LogarithmScale.h
 	inc/MantidAPI/MDGeometry.h
+        inc/MantidAPI/MDFrameValidator.h
 	inc/MantidAPI/MatrixWorkspace.h
 	inc/MantidAPI/MatrixWorkspaceMDIterator.h
 	inc/MantidAPI/MatrixWorkspaceValidator.h
@@ -420,6 +422,7 @@ set ( TEST_FILES
 	LogFilterGeneratorTest.h
 	LogManagerTest.h
 	MDGeometryTest.h
+	MDFrameValidatorTest.h
 	MatrixWorkspaceMDIteratorTest.h
 	ModeratorModelTest.h
 	MuParserUtilsTest.h
diff --git a/Framework/API/inc/MantidAPI/MDFrameValidator.h b/Framework/API/inc/MantidAPI/MDFrameValidator.h
new file mode 100644
index 0000000000000000000000000000000000000000..f34488e7f7397347bbbae2b92ab7b338cc622e6c
--- /dev/null
+++ b/Framework/API/inc/MantidAPI/MDFrameValidator.h
@@ -0,0 +1,55 @@
+#ifndef MANTID_API_MDFRAMEVALIDATOR_H
+#define MANTID_API_MDFRAMEVALIDATOR_H
+
+#include "MantidAPI/DllConfig.h"
+#include "MantidAPI/IMDWorkspace.h"
+#include "MantidKernel/TypedValidator.h"
+
+/**
+  A validator which checks that the frame of the MDWorkspace referred to
+  by a WorkspaceProperty is the expected one.
+
+  Copyright © 2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge
+  National Laboratory & European Spallation Source
+
+  This file is part of Mantid.
+
+  Mantid is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  Mantid 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 General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+  File change history is stored at: <https://github.com/mantidproject/mantid>
+  Code Documentation is available at: <http://doxygen.mantidproject.org>
+*/
+namespace Mantid {
+namespace API {
+class MANTID_API_DLL MDFrameValidator
+    : public Kernel::TypedValidator<IMDWorkspace_sptr> {
+public:
+  explicit MDFrameValidator(const std::string &frameName);
+  /// Gets the type of the validator
+  std::string getType() const { return "mdframe"; }
+  /// Clone the current state
+  Kernel::IValidator_sptr clone() const override;
+
+private:
+  /// Check for validity.
+  std::string checkValidity(const IMDWorkspace_sptr &workspace) const override;
+
+  /// The name of the required frame
+  const std::string m_frameID;
+};
+
+} // namespace API
+} // namespace Mantid
+
+#endif // MANTID_API_MDFRAMEVALIDATOR_H
diff --git a/Framework/API/src/MDFrameValidator.cpp b/Framework/API/src/MDFrameValidator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2165a927cf9cdae71d943995e0e230475f9166dd
--- /dev/null
+++ b/Framework/API/src/MDFrameValidator.cpp
@@ -0,0 +1,42 @@
+#include "MantidAPI/MDFrameValidator.h"
+#include "MantidKernel/IValidator.h"
+#include <boost/make_shared.hpp>
+
+using Mantid::Kernel::IValidator_sptr;
+
+namespace Mantid {
+namespace API {
+
+/** Constructor
+ *
+ * @param frameName :: The name of the frame that the workspace must have.
+ */
+MDFrameValidator::MDFrameValidator(const std::string &frameName)
+    : m_frameID{frameName} {}
+
+/**
+ * Clone the current state
+ */
+Kernel::IValidator_sptr MDFrameValidator::clone() const {
+  return boost::make_shared<MDFrameValidator>(*this);
+}
+
+/** Checks that the frame of the MDWorkspace matches the expected frame.
+ *
+ * @param workspace :: The workspace to test
+ * @return A user level description of the error or "" for no error
+ */
+std::string
+MDFrameValidator::checkValidity(const IMDWorkspace_sptr &workspace) const {
+
+  for (size_t index = 0; index < workspace->getNumDims(); ++index) {
+    const auto &frame = workspace->getDimension(index)->getMDFrame();
+    if (frame.name() != m_frameID)
+      return "MDWorkspace must be in the " + m_frameID + " frame.";
+  }
+
+  return "";
+}
+
+} // namespace API
+} // namespace Mantid
diff --git a/Framework/API/test/MDFrameValidatorTest.h b/Framework/API/test/MDFrameValidatorTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..2896ae18367965499ad116dfd2045d6b9dbdbdde
--- /dev/null
+++ b/Framework/API/test/MDFrameValidatorTest.h
@@ -0,0 +1,76 @@
+#ifndef MANTID_MDUNITVALIDATOR_TEST_H
+#define MANTID_MDUNITVALIDATOR_TEST_H
+
+#include <boost/make_shared.hpp>
+#include <cxxtest/TestSuite.h>
+
+#include "MantidAPI/IMDEventWorkspace_fwd.h"
+#include "MantidAPI/MDFrameValidator.h"
+#include "MantidGeometry/MDGeometry/HKL.h"
+#include "MantidGeometry/MDGeometry/MDFrameFactory.h"
+#include "MantidGeometry/MDGeometry/MDHistoDimension.h"
+#include "MantidGeometry/MDGeometry/QLab.h"
+#include "MantidKernel/UnitLabelTypes.h"
+#include "MantidTestHelpers/FakeObjects.h"
+
+using namespace Mantid::Geometry;
+using namespace Mantid::API;
+using namespace Mantid::Kernel;
+
+class MDFrameValidatorTest : public CxxTest::TestSuite {
+public:
+  // This pair of boilerplate methods prevent the suite being created statically
+  // This means the constructor isn't called when running other tests
+  static MDFrameValidatorTest *createSuite() {
+    return new MDFrameValidatorTest();
+  }
+  static void destroySuite(MDFrameValidatorTest *suite) { delete suite; }
+
+  void testGetType() {
+    MDFrameValidator unitValidator(HKL::HKLName);
+    TS_ASSERT_EQUALS(unitValidator.getType(), "mdframe");
+  }
+
+  void testHKLMDWorkspaceIsValidForValidatorWithHKLFrame() {
+    MDFrameValidator frameValidator(HKL::HKLName);
+
+    HKLFrameFactory factory;
+    auto frame =
+        factory.create(MDFrameArgument{HKL::HKLName, Units::Symbol::RLU});
+    auto dim = boost::make_shared<MDHistoDimension>("x", "x", *frame, 0.0f,
+                                                    100.0f, 10);
+    auto ws = boost::make_shared<MDHistoWorkspaceTester>(dim, dim, dim);
+    TS_ASSERT_EQUALS(frameValidator.isValid(ws), "")
+  };
+
+  void testHKLMDWorkspaceIsNotValidForValidatorWithQLabFrame() {
+    MDFrameValidator frameValidator(QLab::QLabName);
+
+    MDFrameArgument args{HKL::HKLName, Units::Symbol::RLU};
+    auto frame = HKLFrameFactory().create(args);
+    auto dim = boost::make_shared<MDHistoDimension>("x", "x", *frame, 0.0f,
+                                                    100.0f, 10);
+    auto ws = boost::make_shared<MDHistoWorkspaceTester>(dim, dim, dim);
+    TS_ASSERT_EQUALS(frameValidator.isValid(ws),
+                     "MDWorkspace must be in the " + QLab::QLabName + " frame.")
+  };
+
+  void testMixedAxisMDWorkspaceIsNotValidForValidatorWithQLabFrame() {
+    MDFrameValidator frameValidator(QLab::QLabName);
+
+    MDFrameArgument axisArgs1{HKL::HKLName, Units::Symbol::RLU};
+    MDFrameArgument axisArgs2{QLab::QLabName, Units::Symbol::InverseAngstrom};
+
+    auto frame1 = HKLFrameFactory().create(axisArgs1);
+    auto frame2 = QLabFrameFactory().create(axisArgs2);
+    auto dim1 = boost::make_shared<MDHistoDimension>("x", "x", *frame1, 0.0f,
+                                                     100.0f, 10);
+    auto dim2 = boost::make_shared<MDHistoDimension>("x", "x", *frame1, 0.0f,
+                                                     100.0f, 10);
+    auto ws = boost::make_shared<MDHistoWorkspaceTester>(dim1, dim2, dim2);
+    TS_ASSERT_EQUALS(frameValidator.isValid(ws),
+                     "MDWorkspace must be in the " + QLab::QLabName + " frame.")
+  };
+};
+
+#endif // MANTID_MDUNITVALIDATOR_TEST_H
diff --git a/Framework/Kernel/CMakeLists.txt b/Framework/Kernel/CMakeLists.txt
index 878ad4bb5316b6f0b4745bf59294970143cd6e3b..78c1b6bb98747d4cfc6e3da3e3d1a4ad770ab00c 100644
--- a/Framework/Kernel/CMakeLists.txt
+++ b/Framework/Kernel/CMakeLists.txt
@@ -219,7 +219,6 @@ set ( INC_FILES
 	inc/MantidKernel/Logger.h
 	inc/MantidKernel/MDAxisValidator.h
 	inc/MantidKernel/MDUnit.h
-	inc/MantidKernel/MDUnitFactory.h
 	inc/MantidKernel/MRUList.h
 	inc/MantidKernel/MagneticFormFactorTable.h
 	inc/MantidKernel/MagneticIon.h
diff --git a/Framework/PythonInterface/mantid/api/src/Exports/WorkspaceValidators.cpp b/Framework/PythonInterface/mantid/api/src/Exports/WorkspaceValidators.cpp
index 898b1032bfd0a414f110e83b1296a5cd85837d87..52bbdbae877ee847ad4e1dcc761a4d36b23d435d 100644
--- a/Framework/PythonInterface/mantid/api/src/Exports/WorkspaceValidators.cpp
+++ b/Framework/PythonInterface/mantid/api/src/Exports/WorkspaceValidators.cpp
@@ -1,5 +1,7 @@
 #include "MantidAPI/CommonBinsValidator.h"
 #include "MantidAPI/HistogramValidator.h"
+#include "MantidAPI/IMDWorkspace.h"
+#include "MantidAPI/MDFrameValidator.h"
 #include "MantidAPI/NumericAxisValidator.h"
 #include "MantidAPI/RawCountValidator.h"
 #include "MantidAPI/SpectraAxisValidator.h"
@@ -7,6 +9,7 @@
 #include "MantidPythonInterface/kernel/TypedValidatorExporter.h"
 #include <boost/python/class.hpp>
 
+using Mantid::API::IMDWorkspace_sptr;
 using Mantid::Kernel::TypedValidator;
 using Mantid::PythonInterface::TypedValidatorExporter;
 using namespace boost::python;
@@ -17,9 +20,13 @@ void export_MatrixWorkspaceValidator() {
   using Mantid::API::MatrixWorkspaceValidator;
   TypedValidatorExporter<MatrixWorkspace_sptr>::define(
       "MatrixWorkspaceValidator");
+  TypedValidatorExporter<IMDWorkspace_sptr>::define("IMDWorkspaceValidator");
 
   class_<MatrixWorkspaceValidator, bases<TypedValidator<MatrixWorkspace_sptr>>,
          boost::noncopyable>("MatrixWorkspaceValidator", no_init);
+
+  class_<TypedValidator<IMDWorkspace_sptr>, boost::noncopyable>(
+      "IMDWorkspaceValidator", no_init);
 }
 /// Export a validator derived from a MatrixWorkspaceValidator that has a no-arg
 /// constructor
@@ -62,4 +69,14 @@ void export_WorkspaceValidators() {
   EXPORT_WKSP_VALIDATOR_DEFAULT_ARG(
       NumericAxisValidator, int, "axisNumber", 1,
       "Checks whether the axis specified by axisNumber is a NumericAxis");
+
+  class_<MDFrameValidator, bases<TypedValidator<IMDWorkspace_sptr>>,
+         boost::noncopyable>(
+      "MDFrameValidator",
+      init<std::string>(arg("frameName"),
+                        "Checks the MD workspace has the given frame along all "
+                        "dimensions. Accepted values for the `frameName` are "
+                        "currently: `HKL`, `QLab`, `QSample`, `Time of "
+                        "Flight`, `Distance`, `General frame`, `Unknown "
+                        "frame` "));
 }
diff --git a/Framework/PythonInterface/test/python/mantid/api/WorkspaceValidatorsTest.py b/Framework/PythonInterface/test/python/mantid/api/WorkspaceValidatorsTest.py
index f85d754b4681c794cbc49f4f362c4c7f0bb8694a..128b3f48ddfcc8310219d4063d38703a0e47eded 100644
--- a/Framework/PythonInterface/test/python/mantid/api/WorkspaceValidatorsTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/WorkspaceValidatorsTest.py
@@ -9,7 +9,7 @@ from mantid.kernel import IValidator
 from mantid.api import (WorkspaceUnitValidator, HistogramValidator,
                         RawCountValidator, CommonBinsValidator,
                         SpectraAxisValidator, NumericAxisValidator,
-                        InstrumentValidator)
+                        InstrumentValidator, MDFrameValidator)
 
 class WorkspaceValidatorsTest(unittest.TestCase):
 
@@ -67,5 +67,10 @@ class WorkspaceValidatorsTest(unittest.TestCase):
         """
         testhelpers.assertRaisesNothing(self, InstrumentValidator)
 
+    def test_MDFrameValidator_construction(self):
+        testhelpers.assertRaisesNothing(self, MDFrameValidator, "HKL")
+        self.assertRaises(Exception, MDFrameValidator)
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/Framework/TestHelpers/inc/MantidTestHelpers/FakeObjects.h b/Framework/TestHelpers/inc/MantidTestHelpers/FakeObjects.h
index 542ec7189b3dedc7f846f96e792ca1fd98073553..6e69adec50387d9dcb9e66aa7b6a9833621d81ec 100644
--- a/Framework/TestHelpers/inc/MantidTestHelpers/FakeObjects.h
+++ b/Framework/TestHelpers/inc/MantidTestHelpers/FakeObjects.h
@@ -22,6 +22,7 @@
 #include <map>
 #include <string>
 
+#include "MantidAPI/IMDHistoWorkspace.h"
 #include "MantidAPI/ISpectrum.h"
 #include "MantidAPI/ITableWorkspace.h"
 #include "MantidAPI/MatrixWorkspace.h"
@@ -29,12 +30,18 @@
 #include "MantidAPI/SpectraAxis.h"
 #include "MantidGeometry/Instrument.h"
 #include "MantidGeometry/Instrument/DetectorGroup.h"
+#include "MantidGeometry/MDGeometry/MDHistoDimension.h"
+#include "MantidKernel/SpecialCoordinateSystem.h"
 #include "MantidKernel/cow_ptr.h"
 
 using namespace Mantid::API;
+using namespace Mantid::Geometry;
+using Mantid::Kernel::SpecialCoordinateSystem;
+using Mantid::coord_t;
 using Mantid::detid_t;
-using Mantid::specnum_t;
 using Mantid::MantidVec;
+using Mantid::signal_t;
+using Mantid::specnum_t;
 
 //===================================================================================================================
 /** Helper class that implements ISpectrum */
@@ -412,6 +419,308 @@ protected:
   }
 };
 
+//===================================================================================================================
+class MDHistoWorkspaceTester : public IMDHistoWorkspace {
+
+public:
+  uint64_t getNPoints() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+  uint64_t getNEvents() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+
+  std::vector<std::unique_ptr<IMDIterator>> createIterators(
+      size_t suggestedNumCores = 1,
+      Mantid::Geometry::MDImplicitFunction *function = nullptr) const override {
+    UNUSED_ARG(suggestedNumCores)
+    UNUSED_ARG(function)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getSignalAtCoord(
+      const coord_t *coords,
+      const Mantid::API::MDNormalization &normalization) const override {
+    UNUSED_ARG(coords);
+    UNUSED_ARG(normalization);
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getSignalWithMaskAtCoord(
+      const coord_t *coords,
+      const Mantid::API::MDNormalization &normalization) const override {
+    UNUSED_ARG(coords);
+    UNUSED_ARG(normalization);
+    throw std::runtime_error("Not Implemented");
+  }
+
+  void
+  setMDMasking(Mantid::Geometry::MDImplicitFunction *maskingRegion) override {
+    UNUSED_ARG(maskingRegion);
+    throw std::runtime_error("Not Implemented");
+  }
+
+  void clearMDMasking() override {
+    throw std::runtime_error("Not Implemented");
+  }
+
+  SpecialCoordinateSystem getSpecialCoordinateSystem() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+
+  coord_t getInverseVolume() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t *getSignalArray() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t *getErrorSquaredArray() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t *getNumEventsArray() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+
+  void setTo(signal_t signal, signal_t errorSquared,
+             signal_t numEvents) override {
+    UNUSED_ARG(signal);
+    UNUSED_ARG(errorSquared);
+    UNUSED_ARG(numEvents);
+    throw std::runtime_error("Not Implemented");
+  }
+
+  Mantid::Kernel::VMD getCenter(size_t linearIndex) const override {
+    UNUSED_ARG(linearIndex);
+    throw std::runtime_error("Not Implemented");
+  }
+
+  void setSignalAt(size_t index, signal_t value) override {
+    UNUSED_ARG(index)
+    UNUSED_ARG(value)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  void setErrorSquaredAt(size_t index, signal_t value) override {
+    UNUSED_ARG(index)
+    UNUSED_ARG(value)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getErrorAt(size_t index) const override {
+    UNUSED_ARG(index)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getErrorAt(size_t index1, size_t index2) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getErrorAt(size_t index1, size_t index2,
+                      size_t index3) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    UNUSED_ARG(index3)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getErrorAt(size_t index1, size_t index2, size_t index3,
+                      size_t index4) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    UNUSED_ARG(index3)
+    UNUSED_ARG(index4)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getSignalAt(size_t index) const override {
+    UNUSED_ARG(index)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getSignalAt(size_t index1, size_t index2) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getSignalAt(size_t index1, size_t index2,
+                       size_t index3) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    UNUSED_ARG(index3)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getSignalAt(size_t index1, size_t index2, size_t index3,
+                       size_t index4) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    UNUSED_ARG(index3)
+    UNUSED_ARG(index4)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getSignalNormalizedAt(size_t index) const override {
+    UNUSED_ARG(index)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getSignalNormalizedAt(size_t index1, size_t index2) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getSignalNormalizedAt(size_t index1, size_t index2,
+                                 size_t index3) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    UNUSED_ARG(index3)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getSignalNormalizedAt(size_t index1, size_t index2, size_t index3,
+                                 size_t index4) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    UNUSED_ARG(index3)
+    UNUSED_ARG(index4)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getErrorNormalizedAt(size_t index) const override {
+    UNUSED_ARG(index)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getErrorNormalizedAt(size_t index1, size_t index2) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getErrorNormalizedAt(size_t index1, size_t index2,
+                                size_t index3) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    UNUSED_ARG(index3)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t getErrorNormalizedAt(size_t index1, size_t index2, size_t index3,
+                                size_t index4) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    UNUSED_ARG(index3)
+    UNUSED_ARG(index4)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t &errorSquaredAt(size_t index) override {
+    UNUSED_ARG(index)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  signal_t &signalAt(size_t index) override {
+    UNUSED_ARG(index)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  size_t getLinearIndex(size_t index1, size_t index2) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  size_t getLinearIndex(size_t index1, size_t index2,
+                        size_t index3) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    UNUSED_ARG(index3)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  size_t getLinearIndex(size_t index1, size_t index2, size_t index3,
+                        size_t index4) const override {
+    UNUSED_ARG(index1)
+    UNUSED_ARG(index2)
+    UNUSED_ARG(index3)
+    UNUSED_ARG(index4)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  LinePlot getLineData(const Mantid::Kernel::VMD &start,
+                       const Mantid::Kernel::VMD &end,
+                       Mantid::API::MDNormalization normalize) const override {
+    UNUSED_ARG(start)
+    UNUSED_ARG(end)
+    UNUSED_ARG(normalize)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  double &operator[](const size_t &index)override {
+    UNUSED_ARG(index)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  void
+  setCoordinateSystem(const SpecialCoordinateSystem coordinateSystem) override {
+    UNUSED_ARG(coordinateSystem)
+    throw std::runtime_error("Not Implemented");
+  }
+
+  void setDisplayNormalization(
+      const Mantid::API::MDNormalization &preferredNormalization) override {
+    UNUSED_ARG(preferredNormalization);
+    throw std::runtime_error("Not Implemented");
+  }
+
+  // Check if this class has an oriented lattice on any sample object
+  bool hasOrientedLattice() const override {
+    return MultipleExperimentInfos::hasOrientedLattice();
+  }
+
+  size_t getMemorySize() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+
+  const std::string id() const override {
+
+    throw std::runtime_error("Not Implemented");
+  }
+  const std::string &getName() const override {
+
+    throw std::runtime_error("Not Implemented");
+  }
+  bool threadSafe() const override {
+
+    throw std::runtime_error("Not Implemented");
+  }
+  const std::string toString() const override {
+
+    throw std::runtime_error("Not Implemented");
+  }
+  MDHistoWorkspaceTester(MDHistoDimension_sptr dimX, MDHistoDimension_sptr dimY,
+                         MDHistoDimension_sptr dimZ) {
+    std::vector<IMDDimension_sptr> dimensions{dimX, dimY, dimZ};
+    initGeometry(dimensions);
+  }
+
+private:
+  IMDHistoWorkspace *doClone() const override {
+    throw std::runtime_error("Not Implemented");
+  }
+  IMDHistoWorkspace *doCloneEmpty() const override {
+
+    throw std::runtime_error("Not Implemented");
+  }
+};
+
 class VariableBinThrowingTester : public AxeslessWorkspaceTester {
   size_t blocksize() const override {
     if (getSpectrum(0).dataY().size() == getSpectrum(1).dataY().size())
diff --git a/docs/source/release/v3.13.0/framework.rst b/docs/source/release/v3.13.0/framework.rst
index 304588e8554a5d522aa0234b22e79a0a4281b6c2..cadb92ac1d4999a2dbd5aefb3ae7de09052b1825 100644
--- a/docs/source/release/v3.13.0/framework.rst
+++ b/docs/source/release/v3.13.0/framework.rst
@@ -76,6 +76,11 @@ New
 Python
 ------
 
+New
+###
+
+- Added a new ``MDFrameValidator`` which can check that a MD workspace passed to a python algorithm has the expected MD frame (e.g. HKL, QLab, QSample etc.).
+
 Improved
 ########