diff --git a/dune/common/test/CMakeLists.txt b/dune/common/test/CMakeLists.txt
index d176443251e3d21ef59a086988124c61d36b9108..a8a2046966405ffb9d34a87a1c6b733b62049a82 100644
--- a/dune/common/test/CMakeLists.txt
+++ b/dune/common/test/CMakeLists.txt
@@ -25,6 +25,9 @@ dune_add_test(NAME check_fvector_size_fail2
 dune_add_test(SOURCES classnametest.cc
               LINK_LIBRARIES dunecommon)
 
+dune_add_test(SOURCES concept.cc
+              LINK_LIBRARIES dunecommon)
+
 dune_add_test(SOURCES conversiontest.cc)
 
 dune_add_test(SOURCES diagonalmatrixtest.cc
@@ -149,3 +152,9 @@ if(${LAPACK_FOUND})
                 LINK_LIBRARIES dunecommon
                 SKIP_ON_77)
 endif()
+
+install(
+  FILES
+  testsuite.hh
+  collectorstream.hh
+  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/common/test)
diff --git a/dune/common/test/collectorstream.hh b/dune/common/test/collectorstream.hh
new file mode 100644
index 0000000000000000000000000000000000000000..4c31a57c3886825a265f1e03151363c80ef88935
--- /dev/null
+++ b/dune/common/test/collectorstream.hh
@@ -0,0 +1,81 @@
+// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// vi: set et ts=4 sw=2 sts=2:
+#ifndef DUNE_COMMON_TEST_COLLECTORSTREAM_HH
+#define DUNE_COMMON_TEST_COLLECTORSTREAM_HH
+
+#include <sstream>
+#include <string>
+#include <functional>
+
+
+#include <dune/common/typeutilities.hh>
+
+
+namespace Dune {
+
+
+
+/**
+ * \brief Data collector stream
+ *
+ * A class derived from std::ostringstream that allows to
+ * collect data via a temporary returned object. To facilitate
+ * this it stores a callback that is used to pass the collected
+ * data to its creator on destruction.
+ *
+ * In order to avoid passing the same data twice, copy construction
+ * is forbidden and only move construction is allowed.
+ */
+class CollectorStream : public std::ostringstream
+{
+public:
+
+  /**
+   * \brief Create from callback
+   *
+   * \tparam CallBack Type of callback. Must be convertible to std::function<void(std::string)>
+   * \param callBack A copy of this function will be stored and called on destruction.
+   */
+  template<class CallBack,
+    Dune::disableCopyMove<CollectorStream, CallBack> = 0>
+  CollectorStream(CallBack&& callBack) :
+    callBack_(callBack)
+  {}
+
+  CollectorStream(const CollectorStream& other) = delete;
+
+  /**
+   * \brief Move constructor
+   *
+   * This will take over the data and callback from the
+   * moved from CollectorStream and disable the callback
+   * in the latter.
+   */
+  CollectorStream(CollectorStream&& other) :
+    callBack_(other.callBack_)
+  {
+    (*this) << other.str();
+    other.callBack_ = [](std::string){};
+  }
+
+  /**
+   * \brief Destructor
+   *
+   * This calls the callback function given on creation
+   * passing all collected data as a single string argument.
+   */
+  ~CollectorStream()
+  {
+    callBack_(this->str());
+  }
+
+private:
+  std::function<void(std::string)> callBack_;
+};
+
+
+
+} // namespace Dune
+
+
+#endif // DUNE_COMMON_TEST_COLLECTORSTREAM_HH
diff --git a/dune/common/test/concept.cc b/dune/common/test/concept.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5fc00707a8bad6fe5927fcf1d20fa8c1ea786fed
--- /dev/null
+++ b/dune/common/test/concept.cc
@@ -0,0 +1,200 @@
+// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// vi: set et ts=4 sw=2 sts=2:
+#include <config.h>
+
+#include <iostream>
+
+#include <dune/common/parallel/mpihelper.hh>
+#include <dune/common/typelist.hh>
+
+#include <dune/common/concept.hh>
+#include <dune/common/test/testsuite.hh>
+
+
+
+struct HasFoo
+{
+  template<class T>
+  auto require(const T& t) -> decltype(
+    t.foo()
+  );
+};
+
+struct HasBar
+{
+  template<class T>
+  auto require(const T& t) -> decltype(
+    t.bar()
+  );
+};
+
+struct HasFooAndBar1 : Dune::Concept::Refines<HasFoo>
+{
+  template<class T>
+  auto require(const T& t) -> decltype(
+    t.bar()
+  );
+};
+
+struct HasFooAndBar2 : Dune::Concept::Refines<HasBar>
+{
+  template<class T>
+  auto require(const T& t) -> decltype(
+    t.foo()
+  );
+};
+
+struct HasFooAndBar3
+{
+  template<class T>
+  auto require(const T& t) -> decltype(
+    t.foo(),
+    t.bar()
+  );
+};
+
+struct HasFooAndBar4 : Dune::Concept::Refines<HasFoo, HasBar>
+{
+  template<class T>
+  auto require(const T& t) -> decltype(
+    0
+  );
+};
+
+struct HasFooAndBar5
+{
+  template<class T>
+  auto require(const T& t) -> decltype(
+    0
+  );
+  using BaseConceptList = Dune::TypeList<HasFoo, HasBar>;
+};
+
+
+
+
+template<class T>
+struct Foo
+{
+    T foo() const
+    { return T(); }
+};
+
+template<class T>
+struct Bar
+{
+    T bar() const
+    { return T(); }
+};
+
+template<class T>
+struct FooBar
+{
+    T foo() const
+    { return T(); }
+
+    T bar() const
+    { return T(); }
+};
+
+
+
+
+int main ( int argc, char **argv )
+try
+{
+  using namespace Dune;
+
+  MPIHelper::instance(argc, argv);
+
+  TestSuite test;
+
+  test.check(models<HasFoo, Foo<int>>())
+      << "models<HasFoo, Foo<int>>() gives wrong result";
+
+  test.check(not models<HasFoo, Bar<int>>())
+      << "models<HasFoo, Bar<int>>() gives wrong result";
+
+  test.check(models<HasFoo, FooBar<int>>())
+      << "models<HasFoo, FooBar<int>>() gives wrong result";
+
+
+
+  test.check(not models<HasBar, Foo<int>>())
+      << "models<HasBar, Foo<int>>() gives wrong result";
+
+  test.check(models<HasBar, Bar<int>>())
+      << "models<HasBar, Bar<int>>() gives wrong result";
+
+  test.check(models<HasBar, FooBar<int>>())
+      << "models<HasBar, FooBar<int>>() gives wrong result";
+
+
+
+  test.check(not models<HasFooAndBar1, Foo<int>>())
+      << "models<HasFooAndBar1, Foo<int>>() gives wrong result";
+
+  test.check(not models<HasFooAndBar1, Bar<int>>())
+      << "models<HasFooAndBar1, Bar<int>>() gives wrong result";
+
+  test.check(models<HasFooAndBar1, FooBar<int>>())
+      << "models<HasFooAndBar1, FooBar<int>>() gives wrong result";
+
+
+
+  test.check(not models<HasFooAndBar2, Foo<int>>())
+      << "models<HasFooAndBar2, Foo<int>>() gives wrong result";
+
+  test.check(not models<HasFooAndBar2, Bar<int>>())
+      << "models<HasFooAndBar2, Bar<int>>() gives wrong result";
+
+  test.check(models<HasFooAndBar2, FooBar<int>>())
+      << "models<HasFooAndBar2, FooBar<int>>() gives wrong result";
+
+
+
+  test.check(not models<HasFooAndBar3, Foo<int>>())
+      << "models<HasFooAndBar3, Foo<int>>() gives wrong result";
+
+  test.check(not models<HasFooAndBar3, Bar<int>>())
+      << "models<HasFooAndBar3, Bar<int>>() gives wrong result";
+
+  test.check(models<HasFooAndBar3, FooBar<int>>())
+      << "models<HasFooAndBar3, FooBar<int>>() gives wrong result";
+
+
+
+  test.check(not models<HasFooAndBar4, Foo<int>>())
+      << "models<HasFooAndBar4, Foo<int>>() gives wrong result";
+
+  test.check(not models<HasFooAndBar4, Bar<int>>())
+      << "models<HasFooAndBar4, Bar<int>>() gives wrong result";
+
+  test.check(models<HasFooAndBar4, FooBar<int>>())
+      << "models<HasFooAndBar4, FooBar<int>>() gives wrong result";
+
+
+
+  test.check(not models<HasFooAndBar5, Foo<int>>())
+      << "models<HasFooAndBar5, Foo<int>>() gives wrong result";
+
+  test.check(not models<HasFooAndBar5, Bar<int>>())
+      << "models<HasFooAndBar5, Bar<int>>() gives wrong result";
+
+  test.check(models<HasFooAndBar5, FooBar<int>>())
+      << "models<HasFooAndBar5, FooBar<int>>() gives wrong result";
+
+
+
+  return test.exit();
+}
+catch( Dune::Exception &e )
+{
+  std::cerr << "Dune reported error: " << e << std::endl;
+  return 1;
+}
+catch(...)
+{
+  std::cerr << "Unknown exception thrown!" << std::endl;
+  return 1;
+}
diff --git a/dune/common/test/testsuite.hh b/dune/common/test/testsuite.hh
new file mode 100644
index 0000000000000000000000000000000000000000..ff19523657bdcbbd5e7d39fb4eb2209a2970542a
--- /dev/null
+++ b/dune/common/test/testsuite.hh
@@ -0,0 +1,206 @@
+// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// vi: set et ts=4 sw=2 sts=2:
+#ifndef DUNE_COMMON_TEST_TESTSUITE_HH
+#define DUNE_COMMON_TEST_TESTSUITE_HH
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <dune/common/exceptions.hh>
+#include <dune/common/test/collectorstream.hh>
+
+
+
+namespace Dune {
+
+
+
+  /**
+   * \brief A Simple helper class to organize your test suite
+   *
+   * Usage: Construct a TestSuite and call check() or require()
+   * with the condition to check and probably a name for this check.
+   * These methods return a stream such that you can pipe in an
+   * explanantion accomponied by respective data to give a reason
+   * for a test failure.
+   */
+  class TestSuite
+  {
+  public:
+    enum ThrowPolicy
+    {
+      AlwaysThrow,
+      ThrowOnRequired
+    };
+
+    /**
+     * \brief Create TestSuite
+     *
+     * \param name A name to identify this TestSuite. Defaults to "".
+     * \param policy If AlwaysThrow any failing check will throw, otherwise only required checks will do.
+     */
+    TestSuite(ThrowPolicy policy, std::string name="") :
+      name_(name),
+      checks_(0),
+      failedChecks_(0),
+      throwPolicy_(policy==AlwaysThrow)
+    {}
+
+    /**
+     * \brief Create TestSuite
+     *
+     * \param name A name to identify this TestSuite. Defaults to "".
+     * \param policy If AlwaysThrow any failing check will throw, otherwise only required checks will do. Defaults to ThrowOnRequired
+     */
+    TestSuite(std::string name="", ThrowPolicy policy=ThrowOnRequired) :
+      name_(name),
+      checks_(0),
+      failedChecks_(0),
+      throwPolicy_(policy==AlwaysThrow)
+    {}
+
+    /**
+     * \brief Check condition
+     *
+     * This will throw an exception if the check fails and if the AlwaysThrow policy was used on creation.
+     *
+     * \param conditon Checks if this is true and increases the failure counter if not.
+     * \param name A name to identify this check. Defaults to ""
+     * \returns A CollectorStream that can be used to create a diagnostic message to be printed on failure.
+     */
+    CollectorStream check(bool condition, std::string name="")
+    {
+      ++checks_;
+      if (not condition)
+        ++failedChecks_;
+
+      return CollectorStream([=](std::string reason) {
+          if (not condition)
+            this->announceCheckResult(throwPolicy_, "CHECK  ", name, reason);
+        });
+    }
+
+    /**
+     * \brief Check a required condition condition
+     *
+     * This will always throw an exception if the check fails.
+     *
+     * \param conditon Checks if this is true and increases the failure counter if not.
+     * \param name A name to identify this check. Defaults to ""
+     * \returns A CollectorStream that can be used to create a diagnostic message to be printed on failure.
+     */
+    CollectorStream require(bool condition, std::string name="")
+    {
+      ++checks_;
+      if (not condition)
+        ++failedChecks_;
+
+      return CollectorStream([=](std::string reason) {
+          if (not condition)
+            this->announceCheckResult(true, "REQUIRED CHECK", name, reason);
+        });
+    }
+
+    /**
+     * \brief Collect data from a sub-TestSuite
+     *
+     * This will incorporate the accumulated results of the sub-TestSuite
+     * into this one. If the sub-TestSuite failed, i.e., contained failed
+     * checks, a summary will be printed.
+     */
+    void subTest(const TestSuite& subTest)
+    {
+      checks_ += subTest.checks_;
+      failedChecks_ += subTest.failedChecks_;
+
+      if (not subTest)
+        announceCheckResult(throwPolicy_, "SUBTEST", subTest.name(), std::to_string(subTest.failedChecks_)+"/"+std::to_string(subTest.checks_) + " checks failed in this subtest.");
+    }
+
+    /**
+     * \brief Check if this TestSuite failed
+     *
+     * \returns False if any of the executed tests failed, otherwise true.
+     */
+    operator const bool () const
+    {
+      return (failedChecks_==0);
+    }
+
+    /**
+     * \brief Query name
+     *
+     * \returns Name of this TestSuite
+     */
+    std::string name() const
+    {
+      return name_;
+    }
+
+    /**
+     * \brief Print a summary of this TestSuite
+     *
+     * \returns False if any of the executed tests failed, otherwise true.
+     */
+    bool report() const
+    {
+      if (failedChecks_>0)
+        std::cout << composeMessage("TEST   ", name(), std::to_string(failedChecks_)+"/"+std::to_string(checks_) + " checks failed in this test.") << std::endl;
+      return (failedChecks_==0);
+    }
+
+    /**
+     * \brief Exit the test.
+     *
+     * This wil print a summary of the test and return an integer
+     * to be used on program exit.
+     *
+     * \returns 1 if any of the executed tests failed, otherwise 0.
+     */
+    int exit() const
+    {
+      return (report() ? 0: 1);
+    }
+
+  protected:
+
+    // Compose a diagnostic message
+    static std::string composeMessage(std::string type, std::string name, std::string reason)
+    {
+      std::ostringstream s;
+      s << type << " FAILED";
+      if (name!="")
+        s << "(" << name << ")";
+      s << ": ";
+      if (reason!="")
+        s << reason;
+      return s.str();
+    }
+
+    // Announce check results. To be called on failed checks
+    static void announceCheckResult(bool throwException, std::string type, std::string name, std::string reason)
+    {
+      std::string message = composeMessage(type, name, reason);
+      std::cout << message << std::endl;
+      if (throwException)
+      {
+        Dune::Exception ex;
+        ex.message(message);
+        throw ex;
+      }
+    }
+
+    std::string name_;
+    std::size_t checks_;
+    std::size_t failedChecks_;
+    bool throwPolicy_;
+  };
+
+
+
+} // namespace Dune
+
+
+
+#endif // DUNE_COMMON_TEST_TESTSUITE_HH