diff --git a/test/test_crosshair.py b/test/test_crosshair.py
new file mode 100644
index 0000000000000000000000000000000000000000..37daec9ca25702a374d60c3b91a1184e42e54b3b
--- /dev/null
+++ b/test/test_crosshair.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+
+"""
+***************************************************************************
+    
+    ---------------------
+    Date                 : 30.11.2017
+    Copyright            : (C) 2017 by Benjamin Jakimow
+    Email                : benjamin jakimow at geo dot hu-berlin dot de
+***************************************************************************
+*                                                                         *
+*   This program 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 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+***************************************************************************
+"""
+# noinspection PyPep8Naming
+
+from timeseriesviewer.utils import initQgisApplication
+from PyQt5.QtGui import *
+from PyQt5.QtCore import *
+import unittest, tempfile
+
+from timeseriesviewer.mapcanvas import *
+from timeseriesviewer.crosshair import *
+from timeseriesviewer.utils import *
+resourceDir = os.path.join(DIR_REPO,'qgisresources')
+QGIS_APP = initQgisApplication()
+
+SHOW_GUI = True
+
+class testclassDialogTest(unittest.TestCase):
+    """Test rerources work."""
+
+    def setUp(self):
+        """Runs before each test."""
+        pass
+
+    def tearDown(self):
+        """Runs after each test."""
+        pass
+
+    def test_CrosshairWidget(self):
+
+        ds = TestObjects.inMemoryImage()
+
+        lyr = QgsRasterLayer(ds.GetFileList()[0])
+        c = QgsMapCanvas()
+        store = QgsMapLayerStore()
+        store.addMapLayer(lyr)
+        c.setLayers([lyr])
+        c.setDestinationCrs(lyr.crs())
+        c.setExtent(lyr.extent())
+
+        w = CrosshairWidget()
+        self.assertIsInstance(w, CrosshairWidget)
+        self.assertIsInstance(w.mapCanvas, QgsMapCanvas)
+        self.assertTrue(len(w.mapCanvas.layers()), 0)
+
+        w.copyCanvas(c)
+
+
+
+
+
+
+        if SHOW_GUI:
+            QGIS_APP.exec_()
+
+    def test_CrosshairDialog(self):
+
+        pass
+
+
+
+
+if __name__ == "__main__":
+    SHOW_GUI = False
+    unittest.main()
diff --git a/test/test_mapcanvas.py b/test/test_mapcanvas.py
index 6690596f7524e6b49dd007f8b55c4a061998db1d..9c1475efb980add254e86fd83702674c9aeb4951 100644
--- a/test/test_mapcanvas.py
+++ b/test/test_mapcanvas.py
@@ -73,9 +73,6 @@ class testclassDialogTest(unittest.TestCase):
         self.assertTrue(lastPos == p2)
 
 
-    def test_CrosshairDialog(self):
-
-        pass
 
 
 
diff --git a/timeseriesviewer/crosshair.py b/timeseriesviewer/crosshair.py
index b17eff9628dfb4e17d8e074f09e9093034657ea5..6824eaac5acd6d452e48f7d6dd0ffe25ddea9499 100644
--- a/timeseriesviewer/crosshair.py
+++ b/timeseriesviewer/crosshair.py
@@ -404,6 +404,25 @@ class CrosshairWidget(QWidget, loadUI('crosshairwidget.ui')):
         self.refreshCrosshairPreview()
 
 
+    def copyCanvas(self,mapCanvas:QgsMapCanvas):
+        """
+        Copys layers,crs, extent and background color
+        :param mapCanvas:
+        :return:
+        """
+
+        assert isinstance(mapCanvas, QgsMapCanvas)
+        # copy layers
+        canvas = self.w.mapCanvasItem.canvas
+        lyrs = mapCanvas.layers()
+        canvas.setLayers(lyrs)
+        canvas.setDestinationCrs(mapCanvas.mapSettings().destinationCrs())
+        canvas.setExtent(mapCanvas.extent())
+        canvas.setCenter(mapCanvas.center())
+        canvas.setCanvasColor(mapCanvas.canvasColor())
+        self.w.refreshCrosshairPreview()
+
+
 
     def setCanvasColor(self, color):
         self.mapCanvas.setBackgroundColor(color)
@@ -433,6 +452,7 @@ class CrosshairWidget(QWidget, loadUI('crosshairwidget.ui')):
         self.cbCrosshairShowDot.setChecked(style.mShowDot)
         self.cbShowPixelBoundaries.setChecked(style.mShowPixelBorder)
         self.cbShowDistanceMarker.setChecked(style.mShowDistanceMarker)
+
     def crosshairStyle(self):
         style = CrosshairStyle()
         c = self.btnCrosshairColor.color()
@@ -509,16 +529,7 @@ class CrosshairDialog(QgsDialog):
         :param mapCanvas: QgsMapCanvas
         :return:
         """
-        assert isinstance(mapCanvas, QgsMapCanvas)
-        # copy layers
-        canvas = self.w.mapCanvasItem.canvas
-        lyrs = mapCanvas.layers()
-        canvas.setLayers(lyrs)
-        canvas.setDestinationCrs(mapCanvas.mapSettings().destinationCrs())
-        canvas.setExtent(mapCanvas.extent())
-        canvas.setCenter(mapCanvas.center())
-        canvas.setCanvasColor(mapCanvas.canvasColor())
-        self.w.refreshCrosshairPreview()
+        self.w.copyCanvas(mapCanvas)
 
 
 
diff --git a/timeseriesviewer/utils.py b/timeseriesviewer/utils.py
index 9ebc8015d8d295db4ae60cde8729e12b9e559867..8a4acde2650413596f15b69131a11091b9352e29 100644
--- a/timeseriesviewer/utils.py
+++ b/timeseriesviewer/utils.py
@@ -26,12 +26,10 @@ import os, sys, math, re, io, fnmatch
 from collections import defaultdict
 
 from qgis.core import *
-from qgis.core import QgsPointXY, QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsApplication, QgsRectangle, \
-    QgsMapLayer, QgsRasterLayer, QgsVectorLayer, QgsRasterRenderer, QgsFeatureRenderer, QgsRasterDataProvider, QgsUnitTypes, QgsSingleBandPseudoColorRenderer
 
 
 #from qgis.gui import *
-from qgis.gui import QgsMapCanvas, QgisInterface
+from qgis.gui import *
 import qgis.utils
 from PyQt5.QtCore import *
 from PyQt5.QtWidgets import *
@@ -1176,6 +1174,335 @@ def initQgisApplication(pythonPlugins=None, PATH_QGIS=None, qgisDebug=False, qgi
         return qgsApp
 
 
+
+class TestObjects():
+
+    @staticmethod
+    def inMemoryImage(nl=10, ns=20, nb=3, crs='EPSG:32632')->gdal.Dataset:
+        """
+        Create an in-memory gdal.Dataset
+        :param nl:
+        :param ns:
+        :param nb:
+        :param crs:
+        :return:
+        """
+        drv = gdal.GetDriverByName('GTiff')
+        assert isinstance(drv, gdal.Driver)
+
+        path = '/vsimem/testimage.tif'
+        ds = drv.Create(path, ns, nl, bands=nb, eType=gdal.GDT_Float32)
+
+        if isinstance(crs, str):
+            c = QgsCoordinateReferenceSystem(crs)
+            ds.SetProjection(c.toWkt())
+
+        gt = [1000,30,0, \
+              1000,0 ,-30]
+
+        ds.SetGeoTransform(gt)
+        for b in range(1, nb + 1):
+            band = ds.GetRasterBand(b)
+            assert isinstance(band, gdal.Band)
+            array = np.random.random((nl, ns)) - 1
+            band.WriteArray(array)
+        ds.FlushCache()
+        return ds
+
+
+
+    @staticmethod
+    def inMemoryClassification(n=3, nl=10, ns=20, nb=1, crs='EPSG:32632'):
+        from .classificationscheme import ClassificationScheme
+        scheme = ClassificationScheme()
+        scheme.createClasses(n)
+
+        drv = gdal.GetDriverByName('MEM')
+        assert isinstance(drv, gdal.Driver)
+
+
+        ds = drv.Create('', ns, nl, bands=nb, eType=gdal.GDT_Byte)
+
+        if isinstance(crs, str):
+            c = QgsCoordinateReferenceSystem(crs)
+            ds.SetProjection(c.toWkt())
+
+        step = int(np.ceil(float(nl) / len(scheme)))
+
+        assert isinstance(ds, gdal.Dataset)
+        for b in range(1, nb + 1):
+            band = ds.GetRasterBand(b)
+            array = np.zeros((nl, ns), dtype=np.uint8) - 1
+            y0 = 0
+            for i, c in enumerate(scheme):
+                y1 = min(y0 + step, nl - 1)
+                array[y0:y1, :] = c.label()
+                y0 += y1 + 1
+            band.SetCategoryNames(scheme.classNames())
+            band.SetColorTable(scheme.gdalColorTable())
+        ds.FlushCache()
+        return ds
+
+    @staticmethod
+    def qgisInterfaceMockup():
+
+        return QgisMockup()
+
+    @staticmethod
+    def createDropEvent(mimeData:QMimeData):
+        """Creates a QDropEvent conaining the provided QMimeData"""
+        return QDropEvent(QPointF(0, 0), Qt.CopyAction, mimeData, Qt.LeftButton, Qt.NoModifier)
+
+
+    @staticmethod
+    def processingAlgorithm():
+
+        from qgis.core import QgsProcessingAlgorithm
+
+        class TestProcessingAlgorithm(QgsProcessingAlgorithm):
+
+            def __init__(self):
+                super(TestProcessingAlgorithm, self).__init__()
+                s = ""
+
+            def createInstance(self):
+                return TestProcessingAlgorithm()
+
+            def name(self):
+                return 'exmaplealg'
+
+            def displayName(self):
+                return 'Example Algorithm'
+
+            def groupId(self):
+                return 'exampleapp'
+
+            def group(self):
+                return 'TEST APPS'
+
+            def initAlgorithm(self, configuration=None):
+                self.addParameter(QgsProcessingParameterRasterLayer('pathInput', 'The Input Dataset'))
+                self.addParameter(
+                    QgsProcessingParameterNumber('value', 'The value', QgsProcessingParameterNumber.Double, 1, False,
+                                                 0.00, 999999.99))
+                self.addParameter(QgsProcessingParameterRasterDestination('pathOutput', 'The Output Dataset'))
+
+            def processAlgorithm(self, parameters, context, feedback):
+                assert isinstance(parameters, dict)
+                assert isinstance(context, QgsProcessingContext)
+                assert isinstance(feedback, QgsProcessingFeedback)
+
+
+                outputs = {}
+                return outputs
+
+        return TestProcessingAlgorithm()
+
+
+
+    @staticmethod
+    def enmapBoxApplication():
+
+        from enmapbox.gui.applications import EnMAPBoxApplication
+        from enmapbox.gui.enmapboxgui import EnMAPBox
+        enmapbox = EnMAPBox.instance()
+
+        class TestApp(EnMAPBoxApplication):
+            def __init__(self, enmapbox):
+                super(TestApp, self).__init__(enmapbox)
+
+                self.name = 'TestApp'
+                self.licence = 'GPL-3'
+                self.version = '-12345'
+
+            def menu(self, appMenu:QMenu)->QMenu:
+                menu = appMenu.addMenu('Test Menu')
+                action = menu.addAction('Test Action')
+                action.triggered.connect(self.onAction)
+                return menu
+
+            def onAction(self):
+                print('TestApp action called')
+
+            def processingAlgorithms(self):
+                return [TestObjects.processingAlgorithm()]
+
+        return TestApp(enmapbox)
+
+
+
+class QgisMockup(QgisInterface):
+    """
+    A "fake" QGIS Desktop instance that should provide all the inferfaces a plugin developer might need (and nothing more)
+    """
+
+    def pluginManagerInterface(self)->QgsPluginManagerInterface:
+        return self.mPluginManager
+
+    @staticmethod
+    def create()->QgisInterface:
+        """
+        Create the QgisMockup and sets the global variables
+        :return: QgisInterface
+        """
+
+        iface = QgisMockup()
+
+        import qgis.utils
+        # import processing
+        # p = processing.classFactory(iface)
+        if not isinstance(qgis.utils.iface, QgisInterface):
+
+            import processing
+            qgis.utils.iface = iface
+            processing.Processing.initialize()
+
+            import pkgutil
+            prefix = str(processing.__name__ + '.')
+            for importer, modname, ispkg in pkgutil.walk_packages(processing.__path__, prefix=prefix):
+                try:
+                    module = __import__(modname, fromlist="dummy")
+                    if hasattr(module, 'iface'):
+                        print(modname)
+                        module.iface = iface
+                except:
+                    pass
+        #set 'home_plugin_path', which is required from the QGIS Plugin manager
+        assert qgis.utils.iface == iface
+        qgis.utils.home_plugin_path = os.path.join(QgsApplication.instance().qgisSettingsDirPath(), *['python', 'plugins'])
+        return iface
+
+    def __init__(self, *args):
+        # QgisInterface.__init__(self)
+        super(QgisMockup, self).__init__()
+
+        self.mCanvas = QgsMapCanvas()
+        self.mCanvas.blockSignals(False)
+        self.mCanvas.setCanvasColor(Qt.black)
+        self.mCanvas.extentsChanged.connect(self.testSlot)
+        self.mLayerTreeView = QgsLayerTreeView()
+        self.mRootNode = QgsLayerTree()
+        self.mLayerTreeModel = QgsLayerTreeModel(self.mRootNode)
+        self.mLayerTreeView.setModel(self.mLayerTreeModel)
+        self.mLayerTreeMapCanvasBridge = QgsLayerTreeMapCanvasBridge(self.mRootNode, self.mCanvas)
+        self.mLayerTreeMapCanvasBridge.setAutoSetupOnFirstLayer(True)
+
+        import pyplugin_installer.installer
+        PI = pyplugin_installer.instance()
+        self.mPluginManager = QgsPluginManagerMockup()
+
+        self.ui = QMainWindow()
+
+
+
+        self.mMessageBar = QgsMessageBar()
+        mainFrame = QFrame()
+
+        self.ui.setCentralWidget(mainFrame)
+        self.ui.setWindowTitle('QGIS Mockup')
+
+
+        l = QHBoxLayout()
+        l.addWidget(self.mLayerTreeView)
+        l.addWidget(self.mCanvas)
+        v = QVBoxLayout()
+        v.addWidget(self.mMessageBar)
+        v.addLayout(l)
+        mainFrame.setLayout(v)
+        self.ui.setCentralWidget(mainFrame)
+        self.lyrs = []
+        self.createActions()
+
+    def iconSize(self, dockedToolbar=False):
+        return QSize(30,30)
+
+    def testSlot(self, *args):
+        # print('--canvas changes--')
+        s = ""
+
+    def mainWindow(self):
+        return self.ui
+
+
+    def addToolBarIcon(self, action):
+        assert isinstance(action, QAction)
+
+    def removeToolBarIcon(self, action):
+        assert isinstance(action, QAction)
+
+
+    def addVectorLayer(self, path, basename=None, providerkey=None):
+        if basename is None:
+            basename = os.path.basename(path)
+        if providerkey is None:
+            bn, ext = os.path.splitext(basename)
+
+            providerkey = 'ogr'
+        l = QgsVectorLayer(path, basename, providerkey)
+        assert l.isValid()
+        QgsProject.instance().addMapLayer(l, True)
+        self.mRootNode.addLayer(l)
+        self.mLayerTreeMapCanvasBridge.setCanvasLayers()
+        s = ""
+
+    def legendInterface(self):
+        return None
+
+    def addRasterLayer(self, path, baseName=''):
+        l = QgsRasterLayer(path, os.path.basename(path))
+        self.lyrs.append(l)
+        QgsProject.instance().addMapLayer(l, True)
+        self.mRootNode.addLayer(l)
+        self.mLayerTreeMapCanvasBridge.setCanvasLayers()
+        return
+
+        cnt = len(self.canvas.layers())
+
+        self.canvas.setLayerSet([QgsMapCanvasLayer(l)])
+        l.dataProvider()
+        if cnt == 0:
+            self.canvas.mapSettings().setDestinationCrs(l.crs())
+            self.canvas.setExtent(l.extent())
+
+            spatialExtent = SpatialExtent.fromMapLayer(l)
+            # self.canvas.blockSignals(True)
+            self.canvas.setDestinationCrs(spatialExtent.crs())
+            self.canvas.setExtent(spatialExtent)
+            # self.blockSignals(False)
+            self.canvas.refresh()
+
+        self.canvas.refresh()
+
+    def createActions(self):
+        m = self.ui.menuBar().addAction('Add Vector')
+        m = self.ui.menuBar().addAction('Add Raster')
+
+    def mapCanvas(self):
+        return self.mCanvas
+
+    def mapNavToolToolBar(self):
+        super().mapNavToolToolBar()
+
+    def messageBar(self, *args, **kwargs):
+        return self.mMessageBar
+
+    def rasterMenu(self):
+        super().rasterMenu()
+
+    def vectorMenu(self):
+        super().vectorMenu()
+
+    def viewMenu(self):
+        super().viewMenu()
+
+    def windowMenu(self):
+        super().windowMenu()
+
+    def zoomFull(self, *args, **kwargs):
+        super().zoomFull(*args, **kwargs)
+
+
+
 class PythonRunnerImpl(QgsPythonRunner):
     """
     A Qgs PythonRunner implementation