diff --git a/lib/plugins/usermanager/_test/csv_export.test.php b/lib/plugins/usermanager/_test/csv_export.test.php
new file mode 100644
index 0000000000000000000000000000000000000000..d0942a861d73f33e63c9685175297d5ef4243e8a
--- /dev/null
+++ b/lib/plugins/usermanager/_test/csv_export.test.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @group plugin_usermanager
+ * @group admin_plugins
+ * @group plugins
+ * @group bundled_plugins
+ */
+require_once(dirname(__FILE__).'/mocks.class.php');
+
+class plugin_usermanager_csv_export_test extends DokuWikiTest {
+
+    protected  $usermanager;
+
+    function setUp() {
+        $this->usermanager = new admin_mock_usermanager();
+        parent::setUp();
+    }
+
+    /**
+     * based on standard test user/conf setup
+     *
+     * users per _test/conf/users.auth.php
+     * expected to be: testuser:179ad45c6ce2cb97cf1029e212046e81:Arthur Dent:arthur@example.com
+     */
+    function test_export() {
+        $expected = 'User,"Real Name",Email,Groups
+testuser,"Arthur Dent",arthur@example.com,
+';
+        $this->assertEquals($expected, $this->usermanager->tryExport());
+    }
+
+    /**
+     * when configured to use a different locale, the column headings in the first line of the
+     * exported csv data should reflect the langauge strings of that locale
+     */
+    function test_export_withlocale(){
+        global $conf;
+        $old_conf = $conf;
+        $conf['lang'] = 'de';
+
+        $this->usermanager->localised = false;
+        $this->usermanager->setupLocale();
+
+        $conf = $old_conf;
+
+        $expected = 'Benutzername,"Voller Name",E-Mail,Gruppen
+testuser,"Arthur Dent",arthur@example.com,
+';
+        $this->assertEquals($expected, $this->usermanager->tryExport());
+    }
+
+    function test_export_withfilter(){
+        $this->markTestIncomplete(
+            'This test has not been implemented yet.'
+        );
+    }
+
+}
diff --git a/lib/plugins/usermanager/_test/csv_import.test.php b/lib/plugins/usermanager/_test/csv_import.test.php
new file mode 100644
index 0000000000000000000000000000000000000000..5deb201bac53bc300b7a3024650ee56899eaf594
--- /dev/null
+++ b/lib/plugins/usermanager/_test/csv_import.test.php
@@ -0,0 +1,155 @@
+<?php
+
+/**
+ * @group plugin_usermanager
+ * @group admin_plugins
+ * @group plugins
+ * @group bundled_plugins
+ */
+
+require_once(dirname(__FILE__).'/mocks.class.php');
+
+/**
+ *  !!!!! NOTE !!!!!
+ *
+ *  At present, users imported in individual tests remain in the user list for subsequent tests
+ */
+class plugin_usermanager_csv_import_test extends DokuWikiTest {
+
+    private $old_files;
+    protected $usermanager;
+    protected $importfile;
+
+    function setUp() {
+        $this->importfile = tempnam(TMP_DIR, 'csv');
+
+        $this->old_files = $_FILES;
+        $_FILES = array(
+            'import'    =>  array(
+                'name'      =>  'import.csv',
+                'tmp_name'  =>  $this->importfile,
+                'type'      =>  'text/plain',
+                'size'      =>  1,
+                'error'     =>  0,
+            ),
+        );
+
+        $this->usermanager = new admin_mock_usermanager();
+        parent::setUp();
+    }
+
+    function tearDown() {
+        $_FILES = $this->old_files;
+        parent::tearDown();
+    }
+
+    function doImportTest($importCsv, $expectedResult, $expectedNewUsers, $expectedFailures) {
+        global $auth;
+        $before_users = $auth->retrieveUsers();
+
+         io_savefile($this->importfile, $importCsv);
+        $result = $this->usermanager->tryImport();
+
+        $after_users = $auth->retrieveUsers();
+        $import_count = count($after_users) - count($before_users);
+        $new_users = array_diff_key($after_users, $before_users);
+        $diff_users = array_diff_assoc($after_users, $before_users);
+
+        $expectedCount = count($expectedNewUsers);
+
+        $this->assertEquals($expectedResult, $result);                                       // import result as expected
+        $this->assertEquals($expectedCount, $import_count);                                  // number of new users matches expected number imported
+        $this->assertEquals($expectedNewUsers, $this->stripPasswords($new_users));           // new user data matches imported user data
+        $this->assertEquals($expectedCount, $this->countPasswords($new_users));              // new users have a password
+        $this->assertEquals($expectedCount, $this->usermanager->mock_email_notifications_sent);   // new users notified of their passwords
+        $this->assertEquals($new_users, $diff_users);                                        // no other users were harmed in the testing of this import
+        $this->assertEquals($expectedFailures, $this->usermanager->getImportFailures());     // failures as expected
+    }
+
+    function test_import() {
+        $csv = 'User,"Real Name",Email,Groups
+importuser,"Ford Prefect",ford@example.com,user
+';
+        $expected = array(
+            'importuser' => array(
+                'name'  => 'Ford Prefect',
+                'mail'  => 'ford@example.com',
+                'grps'  => array('user'),
+            ),
+        );
+
+        $this->doImportTest($csv, true, $expected, array());
+    }
+
+    function test_importExisting() {
+        $csv = 'User,"Real Name",Email,Groups
+importuser,"Ford Prefect",ford@example.com,user
+';
+        $failures = array(
+            '2' => array(
+                'error' => $this->usermanager->lang['import_error_create'],
+                'user'  => array(
+                    'importuser',
+                    'Ford Prefect',
+                    'ford@example.com',
+                    'user',
+                ),
+                'orig'   => 'importuser,"Ford Prefect",ford@example.com,user'.NL,
+            ),
+        );
+
+        $this->doImportTest($csv, true, array(), $failures);
+    }
+
+    function test_importUtf8() {
+        $csv = 'User,"Real Name",Email,Groups
+importutf8,"Førd Prefect",ford@example.com,user
+';
+        $expected = array(
+            'importutf8' => array(
+                'name'  => 'Førd Prefect',
+                'mail'  => 'ford@example.com',
+                'grps'  => array('user'),
+            ),
+        );
+
+        $this->doImportTest($csv, true, $expected, array());
+    }
+
+    /**
+     *  utf8: u+00F8 (ø) <=> 0xF8 :iso-8859-1
+     */
+    function test_importIso8859() {
+        $csv = 'User,"Real Name",Email,Groups
+importiso8859,"F'.chr(0xF8).'rd Prefect",ford@example.com,user
+';
+        $expected = array(
+            'importiso8859' => array(
+                'name'  => 'Førd Prefect',
+                'mail'  => 'ford@example.com',
+                'grps'  => array('user'),
+            ),
+        );
+
+        $this->doImportTest($csv, true, $expected, array());
+    }
+
+    private function stripPasswords($array){
+        foreach ($array as $user => $data) {
+            unset($array[$user]['pass']);
+        }
+        return $array;
+    }
+
+    private function countPasswords($array){
+        $count = 0;
+        foreach ($array as $user => $data) {
+            if (!empty($data['pass'])) {
+                $count++;
+            }
+        }
+        return $count;
+    }
+
+}
+
diff --git a/lib/plugins/usermanager/_test/mocks.class.php b/lib/plugins/usermanager/_test/mocks.class.php
new file mode 100644
index 0000000000000000000000000000000000000000..edf5438b28ed09c0fcb233c6e5fbd398fd364465
--- /dev/null
+++ b/lib/plugins/usermanager/_test/mocks.class.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ *  test wrapper to allow access to private/protected functions/properties
+ *
+ *  NB: for plugin introspection methods, getPluginType() & getPluginName() to work
+ *      this class name needs to start "admin_" and end "_usermanager".  Internally
+ *      these methods are used in setting up the class, e.g. for language strings
+ */
+class admin_mock_usermanager extends admin_plugin_usermanager {
+
+    public $mock_email_notifications = true;
+    public $mock_email_notifications_sent = 0;
+
+    public function getImportFailures() {
+        return $this->_import_failures;
+    }
+
+    public function tryExport() {
+        ob_start();
+        $this->_export();
+        return ob_get_clean();
+    }
+
+    public function tryImport() {
+        return $this->_import();
+    }
+
+    // no need to send email notifications (mostly)
+    protected function _notifyUser($user, $password, $status_alert=true) {
+        if ($this->mock_email_notifications) {
+            $this->mock_email_notifications_sent++;
+            return true;
+        } else {
+            return parent::_notifyUser($user, $password, $status_alert);
+        }
+    }
+
+    protected function _isUploadedFile($file) {
+        return file_exists($file);
+    }
+}
+