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?


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 :(

1 0 6 403
Log in or sign up to continue

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)

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?

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. 

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()
        Set tRes = tStmt.%Execute("%UnitTest.TestCase")
        While tRes.%Next(.tSC) {
            //TODO: something with tRes.%Get("Name")

If you're filtering by package - and it looks like 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.)

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?

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) {
		Write tRes.%Get("Name"),!



SAMPLES>d ##class(Sample.ClassQueryProperty).%New().Demo()