Written by

Developer at Lite Solutions
Question Francis Galiegue · Apr 25, 2016

Determining whether a class, which you only have the name of, is a unit test class... I know this code sucks; but how to fix it properly?

Hello,

Here's the code:


Method isTestClass(className As %String) As %Boolean
{
	if (className = ..#UTCLASS) {
		return 1
	}
	
	#dim c as %Dictionary.ClassDefinition
	#dim status as %Status
		
	set c = ##class(%Dictionary.ClassDefinition).%OpenId(className,,.status)
	
	if ($$$ISERR(status)) {
		throw ##class(%Exception.StatusException).CreateFromStatus(status)
	}
	
	if ('c.SuperIsDefined()) {
		return 0
	}
	
	#dim children
	#dim len as %Integer
	#dim i as %Integer
	
	set children = $listFromString(c.Super, ",")
	set len = $listLength(children)
	
	for i = 1:1:len {
		if ..isTestClass($listGet(children, i)) {
			return 1
		}
	}
	
	return 0
}

One thing which bothers me here is that I do not "close" the %Dictionary.ClassDefinition object; I believe this to be a problem somewhat, is that the case?

Second, I believe my walking the list of extended classes is not optimal either... Any way to do better?

Finally, and for some reason, I get errors from this code which I can't quite figure out... "Unable to load object", #5809... But from what I see this should not happen :(

Comments

John Murray · Apr 25, 2016

I suggest you use the %IsA classmethod that every class inherits from %Library.SystemBase.

With this I think your method becomes very simple:

 quit $classmethod(className,"%IsA",..#UTCLASS)

0
Francis Galiegue  Apr 25, 2016 to John Murray

Hmm, interesting... But does it work recursively? Ie, if I have class C inherits class B inherits class A, is $classMethod("C", "%IsA", "A") true?

0
Bill Sherman  Apr 26, 2016 to Francis Galiegue

Yes, it does work that way. If Cat is a subclass of Mammal, and Mammal is a subclass of Animal, then ##class(Cat).%IsA("Animal") will return 1. 

0
Timothy Leavitt · Apr 25, 2016

In addition to %IsA (or, similarly, %Extends, which considers multiple inheritance rather than just primary superclasses), the following snippet (slightly modified from an answer I posted on one of your previous questions) may be helpful if you're looking for all of the names of unit test classes:

        Set tStmt = ##class(%SQL.Statement).%New()
        $$$ThrowOnError(tStmt.%PrepareClassQuery("%Dictionary.ClassDefinition","SubclassOf"))
        Set tRes = tStmt.%Execute("%UnitTest.TestCase")
        While tRes.%Next(.tSC) {
            $$$ThrowOnError(tSC)
            //TODO: something with tRes.%Get("Name")
        }
        $$$ThrowOnError(tSC)

If you're filtering by package - and it looks like https://github.com/litesolutions/cache-utcov/blob/master/src/utcov/ClassLookup.cls does this - then you can supply a second argument to the SubclassOf query with the package name for better performance. (i.e., Set tRes = tStmt.%Execute("%UnitTest.TestCase","Some.Package.Name."))

All of these approaches work recursively. (C extends B, B extends A -> C extends A.)

0
Francis Galiegue  Apr 27, 2016 to Timothy Leavitt

Very nice!

One question: since in this code tStmt seems to be a prepared statement, can I just declare it as a parameter to the class and reuse it when I need it?

0
Timothy Leavitt  Apr 27, 2016 to Francis Galiegue

Sure - although it'd be a property, not a parameter. Looking at utcov.ClassLookup (glad to see it's not %utcov now, by the way), this should work fine for you. Here's a sample:

Class Sample.ClassQueryProperty Extends %RegisteredObject
{

Property SubclassQuery As %SQL.Statement [ InitialExpression = {##class(%SQL.Statement).%New()}, Private, ReadOnly ];

Method %OnNew() As %Status [ Private, ServerOnly = 1 ]
{
	Quit ..SubclassQuery.%PrepareClassQuery("%Dictionary.ClassDefinition","SubclassOf")
}

Method Demo()
{
	Set tRes = ..SubclassQuery.%Execute("%UnitTest.TestCase")
	While tRes.%Next(.tSC) {
		$$$ThrowOnError(tSC)
		Write tRes.%Get("Name"),!
	}
	$$$ThrowOnError(tSC)
}

}

Then:

SAMPLES>d ##class(Sample.ClassQueryProperty).%New().Demo()
%UnitTest.IKnowRegression
%UnitTest.PMMLRegression
%UnitTest.SQLDataRegression
%UnitTest.SQLRegression
%UnitTest.SetBuilder
%UnitTest.TSQL
%UnitTest.TestCacheScript
%UnitTest.TestProduction
%UnitTest.TestScript
%UnitTest.TestSqlScript
Wasabi.Logic.Test.InventoryTest
Wasabi.Logic.Test.PricingTest
0