function assertAllSettersForClass(oClass, assert, oClassConstraints) {
		var oMetadata = oClass.getMetadata(),
			sClassName = oMetadata.getName(),
			oControl;

		// Abstract classes should not be tested on their own
		if (oMetadata.isAbstract()) {
			assert.ok(true, "Abstract class '" + sClassName + "' skipped");
			return;
		}

		var aExcludedSettings = oClassConstraints && Array.isArray(oClassConstraints.exclude) ? oClassConstraints.exclude : [];

		var bForceTest4AllMethods = hasUntestedAbstractBaseClass(oClass, assert);

		try {
			oControl = new oClass();
		} catch (e) {
			assert.ok(false, "Failed to instantiate a '" + sClassName + "' with default settings: " + (e.message || e));
			return;
		}


		var pChain = Promise.resolve();
		var iOwnSettings = 0;

		function hasOwnMethod(obj, name) {
			return Object.hasOwn(obj, name)
				|| Object.hasOwn(oClass.prototype, name)
				|| bForceTest4AllMethods;
		}

		function checkOwnMethod(sMethodName, args, sArgs) {
			if ( hasOwnMethod(oControl, sMethodName) ) {
				iOwnSettings++;
				try {
					assert.ok(
						oControl[sMethodName].apply(oControl, args) === oControl,
						sMethodName + "(" + sArgs + ")" + " should always return <this>");
				} catch (e) {
					// we can't distinguish between expected and unexpected errors here, so we log only
					assert.ok(true,
						sMethodName + "(" + sArgs + ")" + " shouldn't throw an exception");
				}
			}
		}

		each(oMetadata.getAllProperties(), function(sPropertyName, oProperty) {

			if ( aExcludedSettings.indexOf(sPropertyName) >= 0 ) {
				assert.ok(true, "ignore property '" + sPropertyName + "'");
				return;
			}

			if ( hasOwnMethod(oControl, oProperty._sMutator) ) {

				iOwnSettings++;

				// Get the value of the property
				var oValue = oProperty.get(oControl);

				// Assert
				try {
					assert.ok(
						oControl === oProperty.set(oControl, oValue),
						oProperty._sMutator + "(...) should always return <this>");
				} catch (e) {
					// If the setter fails we have a special scenario where date may be required
					// but as there is no type "date" in our metadata API we need to identify it here
					// and provide a JavaScript Date so we can test the setter
					var sName = oProperty.name;
					var bDateInName = sName.indexOf("Date", sName.length - 4) !== -1 || sName.substring(0, 4) === "date";
					if ((sName === "date" || bDateInName) && oProperty.type === "object") {
						assert.ok(
							oControl === oProperty.set(oControl, new Date()),
							oProperty._sMutator + "({js date}) should always return <this>");
					} else {
						// If the setter fails for some reason called with the value from get collected before that
						// we need to fail with a meaningful error.
						assert.ok(false,
							oProperty._sMutator + "(" + oValue + ") fails when called " +
							"with value received from get with exception: " + e);
					}
				}
			}
		});

		each(oMetadata.getAllAggregations(), function(sAggregationName, oAggregation) {

			if ( aExcludedSettings.indexOf(sAggregationName) >= 0 ) {
				assert.ok(true, "ignore aggregation '" + sAggregationName + "'");
				return;
			}

			pChain = pChain.then(function() {
				return createAggregatedElement(oAggregation.type);
			}).then(function(oElement) {
				checkOwnMethod(oAggregation._sMutator, [null], "null");
				if ( oElement ) {
					checkOwnMethod(oAggregation._sMutator, [oElement], "elem");
				}
				if ( oAggregation.multiple ) {
					checkOwnMethod(oAggregation._sInsertMutator, [null], "null");
					if ( oElement ) {
						checkOwnMethod(oAggregation._sInsertMutator, [oElement], "elem");
					}
				}
				checkOwnMethod(oAggregation._sDestructor, [], "");
				if ( oElement && !oElement.bIsDestroyed ) {
					try {
						oElement.destroy();
					} catch (e) {
						// ignore
					}
				}
			});

		});

		each(oMetadata.getAllAssociations(), function(sAssociationName, oAssociation) {
			if ( aExcludedSettings.indexOf(sAssociationName) >= 0 ) {
				assert.ok(true, "ignore aggregation '" + sAssociationName + "'");
				return;
			}
			checkOwnMethod(oAssociation._sMutator, [null], "null");
			checkOwnMethod(oAssociation._sMutator, ["dummy"], "ref");
		});

		each(oMetadata.getAllEvents(), function(sEventName, oEvent) {
			if ( aExcludedSettings.indexOf(sEventName) >= 0 ) {
				assert.ok(true, "ignore aggregation '" + sEventName + "'");
				return;
			}
			checkOwnMethod(oEvent._sMutator, [noop], "listener");
			checkOwnMethod(oEvent._sDetachMutator, [noop], "listener");
		});

		pChain = pChain.then(function() {
			try {
				oControl.destroy();
			} catch (e) {
				// ignore errors during destroy if control is known to have issues there
				assert.ok(oClassConstraints && oClassConstraints.failsOnDestroy,
					"failed to destroy '" + oControl + "'" + (oClassConstraints && oClassConstraints.failsOnDestroy ? " (ignored)" : ""));
			}
			if (iOwnSettings === 0 ) {
				assert.ok(true, "no own settings");
			}
		});

		return pChain;
	}
Creates assertions for all setters of the given class.

To avoid redundant checks of inherited base class methods in subclasses, only methods are tested
that either have been attached to the instance itself or directly to the prototype of the instance.
This ensures that when a subclass overrides a method locally, the local method also will be tested.

Abstract base classes are skipped as they often can't be instantiated. To test their 'own' methods, too,
such abstract classes are remembered and the first non-abstract subclass will test all its methods
including the inherited ones, not only its own methods.

@param {function} oClass class to be tested
@param {function} assert QUnit assert
@param {object} oClassConstraints