diff --git a/src/opnsense/mvc/tests/PHPunit.xml b/src/opnsense/mvc/tests/PHPunit.xml
new file mode 100644
index 000000000..9b9506157
--- /dev/null
+++ b/src/opnsense/mvc/tests/PHPunit.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ Ignore
+
+
+
+ ./app
+
+
\ No newline at end of file
diff --git a/src/opnsense/mvc/tests/app/config/config.php b/src/opnsense/mvc/tests/app/config/config.php
new file mode 100644
index 000000000..a9444b5f2
--- /dev/null
+++ b/src/opnsense/mvc/tests/app/config/config.php
@@ -0,0 +1,19 @@
+ array(
+ 'controllersDir' => __DIR__ . '/../../../app/controllers/',
+ 'modelsDir' => __DIR__ . '/../../../app/models/',
+ 'viewsDir' => __DIR__ . '/../../../app/views/',
+ 'pluginsDir' => __DIR__ . '/../../../app/plugins/',
+ 'libraryDir' => __DIR__ . '/../../../app/library/',
+ 'cacheDir' => __DIR__ . '/../../../app/cache/',
+ 'baseUri' => '/opnsense_gui/',
+ ),
+ 'globals' => array(
+ 'config_path' => '/conf/',
+ 'temp_path' => '/tmp/',
+ 'debug' => false,
+ 'simulate_mode' => false
+ )
+));
diff --git a/src/opnsense/mvc/tests/app/models/OPNsense/Base/BaseModel/TestModel.php b/src/opnsense/mvc/tests/app/models/OPNsense/Base/BaseModel/TestModel.php
new file mode 100644
index 000000000..5b185efec
--- /dev/null
+++ b/src/opnsense/mvc/tests/app/models/OPNsense/Base/BaseModel/TestModel.php
@@ -0,0 +1,38 @@
+
+ //tests/OPNsense/TestModel
+
+ OPNsense RecursiveModel test
+
+
+
+
+
+
+ sample@example.com
+ Y
+
+
+
+
diff --git a/src/opnsense/mvc/tests/app/models/OPNsense/Base/BaseModelTest.php b/src/opnsense/mvc/tests/app/models/OPNsense/Base/BaseModelTest.php
new file mode 100644
index 000000000..901308844
--- /dev/null
+++ b/src/opnsense/mvc/tests/app/models/OPNsense/Base/BaseModelTest.php
@@ -0,0 +1,71 @@
+assertInstanceOf('tests\OPNsense\Base\BaseModel\TestModel', BaseModelTest::$model);
+ }
+
+ /**
+ * @depends testCanBeCreated
+ */
+ public function testGeneralAvailable()
+ {
+ $this->assertNotNull(BaseModelTest::$model->general);
+ }
+
+ /**
+ * @depends testGeneralAvailable
+ */
+ public function testCanSetStringValue()
+ {
+ BaseModelTest::$model->general->FromEmail = "test@test.nl";
+ $this->assertEquals(BaseModelTest::$model->general->FromEmail, "test@test.nl");
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ * @expectedExceptionMessage FromEmailXXX not an attribute of general
+ * @depends testGeneralAvailable
+ */
+ public function testCannotSetNonExistingField()
+ {
+ BaseModelTest::$model->general->FromEmailXXX = "test@test.nl";
+ }
+
+}
diff --git a/src/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/Field_Framework_TestCase.php b/src/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/Field_Framework_TestCase.php
new file mode 100644
index 000000000..942057587
--- /dev/null
+++ b/src/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/Field_Framework_TestCase.php
@@ -0,0 +1,89 @@
+getValidators() as $validator) {
+ $validation->add("testfield", $validator);
+ }
+
+ $messages = $validation->validate(array("testfield" => (string)$field));
+ if (count($messages)) {
+ foreach ($messages as $message) {
+ throw new \Phalcon\Validation\Exception($message->getType());
+ }
+ }
+ return;
+ }
+
+ /**
+ * Validate and return exceptions
+ * @param $field feld type
+ * @return array
+ */
+ public function validate($field)
+ {
+ $result = array();
+ $validation = new \Phalcon\Validation();
+ foreach ($field->getValidators() as $validator) {
+ $validation->add("testfield", $validator);
+ }
+
+ $messages = $validation->validate(array("testfield" => (string)$field));
+ if (count($messages)) {
+ foreach ($messages as $message) {
+ $result[] = $message->getType();
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * do not test
+ * @group Ignore
+ */
+ public function testIgnore()
+ {
+ return;
+ }
+}
\ No newline at end of file
diff --git a/src/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/IntegerFieldTest.php b/src/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/IntegerFieldTest.php
new file mode 100644
index 000000000..586fafb28
--- /dev/null
+++ b/src/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/IntegerFieldTest.php
@@ -0,0 +1,89 @@
+assertInstanceOf('\OPNsense\Base\FieldTypes\IntegerField', new IntegerField());
+ }
+
+ /**
+ * @expectedException \Phalcon\Validation\Exception
+ * @expectedExceptionMessage MaxMinValidator
+ */
+ public function testValueLargerThenMax()
+ {
+ $field = new IntegerField();
+ $field->setMaximumValue(100);
+ $field->setMinimumValue(10);
+ $field->setValue("120");
+
+ $this->validateThrow($field);
+ }
+
+ /**
+ * @expectedException \Phalcon\Validation\Exception
+ * @expectedExceptionMessage MaxMinValidator
+ */
+ public function testValueSmallerThenMin()
+ {
+ $field = new IntegerField();
+ $field->setMaximumValue(100);
+ $field->setMinimumValue(10);
+ $field->setValue("5");
+
+ $this->validateThrow($field);
+ }
+
+ /**
+ * not a number
+ */
+ public function testNotANumber()
+ {
+ $field = new IntegerField();
+ $field->setMaximumValue(100);
+ $field->setMinimumValue(10);
+ $field->setValue("5x1");
+
+ $this->assertContains('IntegerValidator', $this->validate($field));
+ }
+}
diff --git a/src/opnsense/mvc/tests/setup.php b/src/opnsense/mvc/tests/setup.php
new file mode 100644
index 000000000..9d93852b3
--- /dev/null
+++ b/src/opnsense/mvc/tests/setup.php
@@ -0,0 +1,51 @@
+set('config', $config);
+
+Di::setDefault($di);