diff --git a/include/grenade/vx/vertex_concept.h b/include/grenade/vx/vertex_concept.h
index 03eb5db95b887d8997b05a7cf667b5d8067797ee..c9c47dbbfc4d502421ace01806247ffcde9f85ca 100644
--- a/include/grenade/vx/vertex_concept.h
+++ b/include/grenade/vx/vertex_concept.h
@@ -33,33 +33,72 @@ template <typename Vertex>
 struct VertexConcept
 {
 	/* Each vertex has a single output port. */
-	static_assert(
-	    std::is_same_v<decltype(&Vertex::output), Port (Vertex::*)() const>,
-	    "Vertex missing output method.");
+	template <typename V, typename = void>
+	struct has_output : std::false_type
+	{};
+	template <typename V>
+	struct has_output<V, std::void_t<decltype(&V::output)>>
+	{
+		constexpr static bool value = std::is_same_v<decltype(&V::output), Port (V::*)() const>;
+	};
+	template <typename V>
+	constexpr static bool has_output_v = has_output<V>::value;
+	static_assert(has_output_v<Vertex>, "Vertex missing output method.");
 
 	/*
 	 * The inputs are to be iterable (`std::array` or `std::vector`) of type `Port`.
 	 */
-	static_assert(
-	    detail::IsInputsReturn<decltype(std::declval<Vertex const>().inputs())>::value,
-	    "Vertex missing inputs method.");
+	template <typename V, typename = void>
+	struct has_inputs : std::false_type
+	{};
+	template <typename V>
+	struct has_inputs<V, std::void_t<decltype(&V::inputs)>>
+	{
+		constexpr static bool value =
+		    detail::IsInputsReturn<decltype(std::declval<V const>().inputs())>::value;
+	};
+	template <typename V>
+	constexpr static bool has_inputs_v = has_inputs<V>::value;
+	static_assert(has_inputs_v<Vertex>, "Vertex missing inputs method.");
 
 	/*
 	 * The last element of the input ports can be variadic meaning it can be extended to an
 	 * arbitrary number (including zero) of distinct ports. For this to be the case `variadic_input`
 	 * has to be set to true.
 	 */
-	static_assert(
-	    std::is_same_v<decltype(Vertex::variadic_input), bool const>,
-	    "Vertex missing variadic_input specifier.");
+	template <typename V, typename = void>
+	struct has_variadic_input : std::false_type
+	{};
+	template <typename V>
+	struct has_variadic_input<V, std::void_t<decltype(&V::variadic_input)>>
+	{
+		constexpr static bool value = std::is_same_v<decltype(V::variadic_input), bool const>;
+	};
+	template <typename V>
+	constexpr static bool has_variadic_input_v = has_variadic_input<V>::value;
+	static_assert(has_variadic_input_v<Vertex>, "Vertex missing variadic_input specifier.");
 
 	/*
 	 * Vertices which are allowed connections between different execution instances are to set
 	 * `can_connect_different_execution_instances` to true.
 	 * TODO: this is rather a ConnectionType property, right?
 	 */
+	template <typename V, typename = void>
+	struct has_can_connect_different_execution_instances : std::false_type
+	{};
+	template <typename V>
+	struct has_can_connect_different_execution_instances<
+	    V,
+	    std::void_t<decltype(&V::can_connect_different_execution_instances)>>
+	{
+		constexpr static bool value =
+		    std::is_same_v<decltype(V::can_connect_different_execution_instances), bool const>;
+	};
+	template <typename V>
+	constexpr static bool has_can_connect_different_execution_instances_v =
+	    has_can_connect_different_execution_instances<V>::value;
 	static_assert(
-	    std::is_same_v<decltype(Vertex::can_connect_different_execution_instances), bool const>,
+	    has_can_connect_different_execution_instances_v<Vertex>,
 	    "Vertex missing can_connect_different_execution_instances specifier.");
 
 	/**
diff --git a/tests/sw/grenade/vx/test-vertex_concept.cpp b/tests/sw/grenade/vx/test-vertex_concept.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c18b33be4b15d0e7194cfda3cf4d4b5fd03332da
--- /dev/null
+++ b/tests/sw/grenade/vx/test-vertex_concept.cpp
@@ -0,0 +1,84 @@
+#include <gtest/gtest.h>
+
+#include "grenade/vx/port.h"
+#include <stdexcept>
+#include <vector>
+
+/**
+ * Drop-in replacement for `static_assert` performing checks at runtime.
+ */
+struct exceptionalized_static_assert
+{
+	exceptionalized_static_assert(bool const cond, char const* what)
+	{
+		if (cond) {
+			static_cast<void>(what);
+		} else {
+			throw std::logic_error(what);
+		}
+	}
+
+	exceptionalized_static_assert(bool const cond) :
+	    exceptionalized_static_assert(cond, "static_assert failed.")
+	{}
+};
+
+#define TOKENPASTE(x, y) x##y
+#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
+/**
+ * Replace `static_assert` by runtime checks in order to be able to unittest failures.
+ */
+#define static_assert(cond, ...)                                                                   \
+	exceptionalized_static_assert TOKENPASTE2(_test_, __COUNTER__)                                 \
+	{                                                                                              \
+		cond, __VA_ARGS__                                                                          \
+	}
+
+#include "grenade/vx/vertex_concept.h"
+
+using namespace grenade::vx;
+
+struct IncorrectOutput
+{
+	void output() const;
+};
+
+struct IncorrectInputs
+{
+	Port output() const;
+
+	std::vector<int> inputs() const;
+};
+
+struct IncorrectVariadicInput
+{
+	Port output() const;
+	std::vector<Port> inputs() const;
+
+	int const variadic_input;
+};
+
+struct IncorrectCanConnectDifferentExecutionInstances
+{
+	Port output() const;
+	std::vector<Port> inputs() const;
+	bool const variadic_input;
+
+	int const can_connect_different_execution_instances;
+};
+
+struct Correct
+{
+	Port output() const;
+	std::vector<Port> inputs() const;
+	bool const variadic_input;
+	bool const can_connect_different_execution_instances;
+};
+
+TEST(VertexConcept, General)
+{
+	EXPECT_THROW(VertexConcept<IncorrectOutput>{}, std::logic_error);
+	EXPECT_THROW(VertexConcept<IncorrectInputs>{}, std::logic_error);
+	EXPECT_THROW(VertexConcept<IncorrectVariadicInput>{}, std::logic_error);
+	EXPECT_THROW(VertexConcept<IncorrectCanConnectDifferentExecutionInstances>{}, std::logic_error);
+}