Index: src/sas/qtgui/GUITests.py
===================================================================
--- src/sas/qtgui/GUITests.py	(revision 676f1378496edab35bd11ed8e0ce2614c0db17b6)
+++ src/sas/qtgui/GUITests.py	(revision da9a07226e9e59e05151f1ed443b9554788d1294)
@@ -57,4 +57,5 @@
 from Perspectives.Fitting.UnitTesting import FittingOptionsTest
 from Perspectives.Fitting.UnitTesting import MultiConstraintTest
+from Perspectives.Fitting.UnitTesting import ComplexConstraintTest
 from Perspectives.Fitting.UnitTesting import ConstraintWidgetTest
 
@@ -118,4 +119,5 @@
         unittest.makeSuite(MultiConstraintTest.MultiConstraintTest,       'test'),
         unittest.makeSuite(ConstraintWidgetTest.ConstraintWidgetTest,     'test'),
+        unittest.makeSuite(ComplexConstraintTest.ComplexConstraintTest,   'test'),
 
         #  Invariant
Index: src/sas/qtgui/Perspectives/Fitting/ComplexConstraint.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/ComplexConstraint.py	(revision 07645932c23d39791c9864289a850f8ef2d3afa5)
+++ src/sas/qtgui/Perspectives/Fitting/ComplexConstraint.py	(revision da9a07226e9e59e05151f1ed443b9554788d1294)
@@ -22,5 +22,4 @@
 
         self.setupUi(self)
-        #self.setFixedSize(self.minimumSizeHint())
         self.setModal(True)
 
@@ -34,5 +33,5 @@
         self.setupWidgets()
         self.setupSignals()
-        #self.setupTooltip()
+        self.setupTooltip()
 
         self.setFixedSize(self.minimumSizeHint())
@@ -86,6 +85,8 @@
         Tooltip for txtConstraint
         """
-        tooltip = "E.g.\n%s = 2.0 * (%s)\n" %(self.params[0], self.params[1])
-        tooltip += "%s = sqrt(%s) + 5"%(self.params[0], self.params[1])
+        p1 = self.tab_names[0] + ":" + self.cbParam1.currentText()
+        p2 = self.tab_names[1]+"."+self.cbParam2.currentText()
+        tooltip = "E.g.\n%s = 2.0 * (%s)\n" %(p1, p2)
+        tooltip += "%s = sqrt(%s) + 5"%(p1, p2)
         self.txtConstraint.setToolTip(tooltip)
 
@@ -130,5 +131,5 @@
         self.cbParam2.setCurrentIndex(index1)
         self.cbOperator.setCurrentIndex(indexOp)
-        #self.setupTooltip()
+        self.setupTooltip()
 
     def validateFormula(self):
Index: src/sas/qtgui/Perspectives/Fitting/ConstraintWidget.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/ConstraintWidget.py	(revision 17968c36288857edfa0dab40378ad2ae5f84c130)
+++ src/sas/qtgui/Perspectives/Fitting/ConstraintWidget.py	(revision da9a07226e9e59e05151f1ed443b9554788d1294)
@@ -357,4 +357,5 @@
         Determines if the tab can be imported and included in the widget
         """
+        if not isinstance(tab, str): return False
         if not self.currentType in tab: return False
         object = ObjectLibrary.getObject(tab)
@@ -396,11 +397,8 @@
         menu.addSeparator()
 
-        #menu.addAction(self.actionRemoveConstraint)
         if num_rows >= 2:
             menu.addAction(self.actionMutualMultiConstrain)
 
         # Define the callbacks
-        #self.actionConstrain.triggered.connect(self.addSimpleConstraint)
-        #self.actionRemoveConstraint.triggered.connect(self.deleteConstraint)
         self.actionMutualMultiConstrain.triggered.connect(self.showMultiConstraint)
         self.actionSelect.triggered.connect(self.selectModels)
@@ -443,7 +441,5 @@
 
         # Define the callbacks
-        #self.actionConstrain.triggered.connect(self.addSimpleConstraint)
         self.actionRemoveConstraint.triggered.connect(self.deleteConstraint)
-        #self.actionMutualMultiConstrain.triggered.connect(self.showMultiConstraint)
         self.actionSelect.triggered.connect(self.selectConstraints)
         self.actionDeselect.triggered.connect(self.deselectConstraints)
Index: src/sas/qtgui/Perspectives/Fitting/UnitTesting/ComplexConstraintTest.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/UnitTesting/ComplexConstraintTest.py	(revision da9a07226e9e59e05151f1ed443b9554788d1294)
+++ src/sas/qtgui/Perspectives/Fitting/UnitTesting/ComplexConstraintTest.py	(revision da9a07226e9e59e05151f1ed443b9554788d1294)
@@ -0,0 +1,148 @@
+import sys
+import unittest
+import numpy as np
+import webbrowser
+
+from unittest.mock import MagicMock
+
+from PyQt5 import QtGui, QtWidgets
+from PyQt5 import QtWebKit
+
+# set up import paths
+import path_prepare
+
+from sas.qtgui.Utilities.GuiUtils import Communicate
+
+# Local
+from sas.qtgui.Perspectives.Fitting.FittingWidget import FittingWidget
+from sas.qtgui.Perspectives.Fitting.ComplexConstraint import ComplexConstraint
+
+#if not QtWidgets.QApplication.instance():
+#    app = QtWidgets.QApplication(sys.argv)
+app = QtWidgets.QApplication(sys.argv)
+
+class dummy_manager(object):
+    HELP_DIRECTORY_LOCATION = "html"
+    communicate = Communicate()
+
+class ComplexConstraintTest(unittest.TestCase):
+    '''Test the ComplexConstraint dialog'''
+    def setUp(self):
+        '''Create ComplexConstraint dialog'''
+        # mockup tabs
+        self.tab1 = FittingWidget(dummy_manager())
+        self.tab2 = FittingWidget(dummy_manager())
+        # set some models on tabs
+        category_index = self.tab1.cbCategory.findText("Shape Independent")
+        self.tab1.cbCategory.setCurrentIndex(category_index)
+        category_index = self.tab2.cbCategory.findText("Cylinder")
+        self.tab2.cbCategory.setCurrentIndex(category_index)
+
+        tabs = [self.tab1, self.tab2]
+        self.widget = ComplexConstraint(parent=None, tabs=tabs)
+
+    def tearDown(self):
+        '''Destroy the GUI'''
+        self.widget.close()
+        self.widget = None
+
+    def testDefaults(self):
+        '''Test the GUI in its default state'''
+        self.assertIsInstance(self.widget, QtWidgets.QDialog)
+        # Default title
+        self.assertEqual(self.widget.windowTitle(), "Complex Constraint")
+
+        # Modal window
+        self.assertTrue(self.widget.isModal())
+
+        # initial tab names
+        self.assertEqual(self.widget.tab_names, ['M1','M1'])
+        self.assertIn('scale', self.widget.params[0])
+        self.assertIn('background', self.widget.params[1])
+
+    def testLabels(self):
+        ''' various labels on the widget '''
+        # params related setup
+        self.assertEqual(self.widget.txtConstraint.text(), 'M1.scale')
+        self.assertEqual(self.widget.txtOperator.text(), '=')
+        self.assertEqual(self.widget.txtName1.text(), 'M1')
+        self.assertEqual(self.widget.txtName2.text(), 'M1')
+
+    def testTooltip(self):
+        ''' test the tooltip'''
+        p1 = self.widget.tab_names[0] + ":" + self.widget.cbParam1.currentText()
+        p2 = self.widget.tab_names[1]+"."+self.widget.cbParam2.currentText()
+
+        tooltip = "E.g.\n%s = 2.0 * (%s)\n" %(p1, p2)
+        tooltip += "%s = sqrt(%s) + 5"%(p1, p2)
+        self.assertEqual(self.widget.txtConstraint.toolTip(), tooltip)
+
+    def testValidateFormula(self):
+        ''' assure enablement and color for valid formula '''
+        # Invalid string
+        self.widget.validateConstraint = MagicMock(return_value=False)
+        self.widget.validateFormula()
+        style_sheet = "QLineEdit {background-color: red;}"
+        self.assertFalse(self.widget.cmdOK.isEnabled())
+        self.assertEqual(self.widget.txtConstraint.styleSheet(),style_sheet)
+
+        # Valid string
+        self.widget.validateConstraint = MagicMock(return_value=True)
+        self.widget.validateFormula()
+        style_sheet = "QLineEdit {background-color: white;}"
+        self.assertTrue(self.widget.cmdOK.isEnabled())
+        self.assertEqual(self.widget.txtConstraint.styleSheet(),style_sheet)
+
+    def testValidateConstraint(self):
+        ''' constraint validator test'''
+        #### BAD
+        # none
+        self.assertFalse(self.widget.validateConstraint(None))
+        # inf
+        self.assertFalse(self.widget.validateConstraint(np.inf))
+        # 0
+        self.assertFalse(self.widget.validateConstraint(0))
+        # ""
+        self.assertFalse(self.widget.validateConstraint(""))
+        # p2_
+        self.assertFalse(self.widget.validateConstraint("M2.scale_"))
+        # p1
+        self.assertFalse(self.widget.validateConstraint("M2.scale"))
+
+        ### GOOD
+        # p2
+        self.assertTrue(self.widget.validateConstraint("scale"))
+        # " p2    "
+        self.assertTrue(self.widget.validateConstraint(" scale    "))
+        # sqrt(p2)
+        self.assertTrue(self.widget.validateConstraint("sqrt(scale)"))
+        # -p2
+        self.assertTrue(self.widget.validateConstraint("-scale"))
+        # log10(p2) - sqrt(p2) + p2
+        self.assertTrue(self.widget.validateConstraint("log10(scale) - sqrt(scale) + scale"))
+        # log10(    p2    ) +  p2
+        self.assertTrue(self.widget.validateConstraint("log10(    scale    ) +  scale  "))
+
+    def testConstraint(self):
+        """
+        Test the return of specified constraint
+        """
+        # default data
+        self.assertEqual(self.widget.constraint(), ('M1', 'scale', '=', 'M1.scale'))
+
+        # Change parameter and operand
+        self.widget.cbOperator.setCurrentIndex(3)
+        self.widget.cbParam1.setCurrentIndex(3)
+        self.assertEqual(self.widget.constraint(), ('M1', 'bjerrum_length', '>=', 'M1.scale'))
+
+    def testOnHelp(self):
+        """
+        Test the default help renderer
+        """
+        webbrowser.open = MagicMock()
+
+        # invoke the tested method
+        self.widget.onHelp()
+
+        # see that webbrowser open was attempted
+        webbrowser.open.assert_called_once()
Index: src/sas/qtgui/Perspectives/Fitting/UnitTesting/ConstraintWidgetTest.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/UnitTesting/ConstraintWidgetTest.py	(revision da9a07226e9e59e05151f1ed443b9554788d1294)
+++ src/sas/qtgui/Perspectives/Fitting/UnitTesting/ConstraintWidgetTest.py	(revision da9a07226e9e59e05151f1ed443b9554788d1294)
@@ -0,0 +1,89 @@
+import sys
+import unittest
+import numpy as np
+
+from unittest.mock import MagicMock
+
+from PyQt5 import QtGui, QtCore, QtWidgets
+from PyQt5 import QtWebKit
+
+# set up import paths
+import path_prepare
+
+import sas.qtgui.Utilities.ObjectLibrary as ObjectLibrary
+
+# Local
+from sas.qtgui.Perspectives.Fitting.ConstraintWidget import ConstraintWidget
+from sas.qtgui.Perspectives.Fitting.Constraints import Constraint
+
+if not QtWidgets.QApplication.instance():
+    app = QtWidgets.QApplication(sys.argv)
+
+class ConstraintWidgetTest(unittest.TestCase):
+    '''Test the ConstraintWidget dialog'''
+    def setUp(self):
+        '''Create ConstraintWidget dialog'''
+        class dummy_perspective(QtWidgets.QTabWidget):
+            tabsModifiedSignal = QtCore.pyqtSignal()
+            def __init__(self):
+                super(dummy_perspective, self).__init__()
+                pass
+
+        self.widget = ConstraintWidget(parent=dummy_perspective())
+
+        # Example constraint object
+        self.constraint1 = Constraint(parent=None, param="test", value="7.0", min="0.0", max="inf", func="M1:sld")
+        self.constraint2 = Constraint(parent=None, param="poop", value="7.0", min="0.0", max="inf", func="7.0")
+
+    def tearDown(self):
+        '''Destroy the GUI'''
+        self.widget.close()
+        self.widget = None
+
+    def testDefaults(self):
+        '''Test the GUI in its default state'''
+        self.assertIsInstance(self.widget, QtWidgets.QWidget)
+        # Default title
+        self.assertEqual(self.widget.windowTitle(), "Constrained and Simultaneous Fit")
+        # Dicts
+        self.assertIsInstance(self.widget.available_constraints, dict)
+        self.assertIsInstance(self.widget.available_tabs, dict)
+        # TableWidgets
+        self.assertEqual(self.widget.tblTabList.columnCount(), 4)
+        self.assertEqual(self.widget.tblConstraints.columnCount(), 1)
+
+    def testLabels(self):
+        ''' various labels on the widget '''
+        # params related setup
+        pass
+
+    def testTooltip(self):
+        ''' test the tooltip'''
+        pass
+
+    def testIsTabImportable(self):
+        ''' tab checks for consistency '''
+        test_tab = QtCore.QObject()
+        test_tab.data = self.constraint1
+        ObjectLibrary.getObject = MagicMock(return_value=test_tab)
+
+        self.assertFalse(self.widget.isTabImportable(None))
+        self.assertTrue(self.widget.isTabImportable("FitTab333333"))
+        self.assertFalse(self.widget.isTabImportable("BatchTab"))
+        self.assertFalse(self.widget.isTabImportable("BatchTab"))
+
+        pass
+
+    def testUpdateFitLine(self):
+        ''' See if the fit table row can be updated '''
+        pass
+
+    def testUpdateFitList(self):
+        ''' see if the fit table can be updated '''
+        pass
+
+    def testUpdateConstraintList(self):
+        ''' see if the constraint table can be updated '''
+        pass
+
+
Index: src/sas/qtgui/Perspectives/Fitting/UnitTesting/MultiConstraintTest.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/UnitTesting/MultiConstraintTest.py	(revision 0595bb7954497ab0a25be7ffb88395a048df52ae)
+++ src/sas/qtgui/Perspectives/Fitting/UnitTesting/MultiConstraintTest.py	(revision da9a07226e9e59e05151f1ed443b9554788d1294)
@@ -2,4 +2,5 @@
 import unittest
 import numpy as np
+import webbrowser
 
 from unittest.mock import MagicMock
@@ -98,2 +99,14 @@
         # log10(    p2    ) +  p2
         self.assertTrue(self.widget.validateConstraint("log10(    p2    ) +  p2  "))
+
+    def testOnHelp(self):
+        """
+        Test the default help renderer
+        """
+        webbrowser.open = MagicMock()
+
+        # invoke the tested method
+        self.widget.onHelp()
+
+        # see that webbrowser open was attempted
+        webbrowser.open.assert_called_once()
