HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Unit Testing

10-06-2007, 08:49 PM#1
PipeDream
Unit testing is a common practice intended to check that each layer of your code is probably correct. Typically it's not so important for warcraft because map code is shallow and non cyclical; each trigger is mostly unrelated to the next. Nonetheless I found it very helpful when doing updating rounds on systems.

The goal is to automatically run a bunch of tests for your functions when debug mode is enabled. Here's a small library I'm using to help out:
Collapse JASS:
library Test initializer RunTests
    function interface proposition takes nothing returns boolean

    public function Closeto takes real x, real y returns boolean
        return RAbsBJ(x - y) <= 0.001
    endfunction

    private function prop_Closeto takes nothing returns boolean
        return Closeto(1,1) and Closeto(1,1.0005) and not Closeto(0,1)
    endfunction
    
    public function DebugMsg takes string s returns nothing
        call BJDebugMsg(s)
        call Cheat("DebugMsg: "+s)
    endfunction  
   
    public function Assert takes string name, proposition t returns nothing
        if t.evaluate() then
            call BJDebugMsg("|cff00ff00"+"OK  "+"|r"+name)
            call Cheat("DebugMsg: OK   "+name)
        else
            call DebugMsg("|cffff0000"+"Failed  "+"|r"+name)
            call Cheat("DebugMsg: Failed  "+name)
        endif
    endfunction
    
    private function RunTests takes nothing returns nothing
        debug local string n = "SelfTest "
        debug call Test_Assert(n+"Closeto",proposition.prop_Closeto)
    endfunction
endlibrary
Use:
  • Add initializer RunTests to your library and require Test
  • Write prop_X functions which take nothing and return boolean
  • Add them to the private RunTests function in your library with debug call Test_Assert("Libname"+"X",proposition.prop_X)
prop_Closeto and RunTests is an example of how to use it. You can find more in my save system. Yes, the first time I wrote Closeto there was a bug which it found-I forgot the RAbsBJ so Closeto(0,1) would fail.

One thing that sucks about doing things this way is that just returning false when a test fails is pretty lame. You have to go through commenting and uncommenting each return in the function until you find the failure. It needs something like return ("this failed test",false) or throw out propositions and the function passing and use assert inside the test cases.

Prepending every line with debug sucks, and the RunTests function gets called anyway.

What sort of preprocessing power would make this easier? Are there more generic functions like Closeto that every tester should have?
10-07-2007, 09:39 AM#2
PitzerMike
The best idea I have is adding out parameters (or ref parameters).
An option that doesn't need preprocessor changes is changing it to return a string and interpreting an empty string as true.
I'm not going to suggest throw/catch *shudders*

As for utility functions: You'll often want to display rawcodes, so Rawcode2Integer and Integer2Rawcode functions may be a good addition.

I don't know if people still use I2H, if that's the case an IsValid<Handle> function can be nice. However I don't know if this can be implemented for certain handle types (timers, triggers). At least it's possible for widgets (GetTypeId function)