{"id":126,"date":"2009-11-06T20:44:44","date_gmt":"2009-11-06T19:44:44","guid":{"rendered":"http:\/\/www.gennard.net\/blog\/?p=126"},"modified":"2009-11-06T20:44:44","modified_gmt":"2009-11-06T19:44:44","slug":"nunit-and-cobol-net","status":"publish","type":"post","link":"http:\/\/www.gennard.net\/blog\/2009\/11\/nunit-and-cobol-net\/","title":{"rendered":"NUnit and COBOL.Net"},"content":{"rendered":"<p>A while back, I spent a afternoon converting some C# NUnit documentation into COBOL .Net, so I thought I would share the document with the world, with the hope that it will help any one interested in using NUnit and COBOL.<\/p>\n<p>Enjoy!<\/p>\n<hr>\n<p>Let\u2019s start with a simple example. Suppose we are writing a bank application and we have a basic domain class \u2013 Account. Account supports operations to deposit, withdraw, and transfer funds. The Account class may look like this:<\/p>\n<p><code lang=\"cobol\" width=\"700\" lines=\"-1\" nowrap=\"0\" >       class-id. Account as \"Account\".<br \/>\n       environment division.<br \/>\n       configuration section.<br \/>\n       repository.<br \/>\n       object.<br \/>\n       data division.<br \/>\n       working-storage section.<br \/>\n\t   01 balance comp-2 value 0 property as \"Balance\".<\/p>\n<p>       method-id. \"Deposit\".<br \/>\n       local-storage section.<br \/>\n       linkage section.<br \/>\n       01 lnk-amount comp-2.<br \/>\n       procedure division using by value lnk-amount.<br \/>\n           add lnk-amount to balance<br \/>\n           exit method.<br \/>\n       end method \"Deposit\".<\/p>\n<p>       method-id. \"Withdraw\".<br \/>\n       local-storage section.<br \/>\n       linkage section.<br \/>\n       01 lnk-amount comp-2.<br \/>\n       procedure division using by value lnk-amount.<br \/>\n           subtract lnk-amount from balance<br \/>\n           exit method.<br \/>\n       end method \"Withdraw\".<\/p>\n<p>       method-id. \"TransferFunds\".<br \/>\n       local-storage section.<br \/>\n       linkage section.<br \/>\n       01 lnk-Account\tobject reference Account.<br \/>\n       01 lnk-amount    comp-2.<br \/>\n       procedure division using by value lnk-Account,<br \/>\n                                by value lnk-amount.<br \/>\n           exit method.<br \/>\n       end method \"TransferFunds\".<br \/>\n       end object.<br \/>\n       end class Account.<br \/>\n<\/code><\/p>\n<p>Now let\u2019s write a test for this class \u2013 AccountTest. The first method we will test is TransferFunds.<\/p>\n<p><code lang=\"cobol\" width=\"700\" lines=\"-1\" nowrap=\"0\" >      $set preservecase<br \/>\n       class-id. AccountTest as \"AccountTest\".<\/p>\n<p>       environment division.<br \/>\n       configuration section.<br \/>\n       repository.<br \/>\n        class sys-single\tas \"System.Single\"<br \/>\n        class cls-TestFixture as \"NUnit.Framework.TestFixtureAttribute\"<br \/>\n        class cls-Test\t\t  as \"NUnit.Framework.TestAttribute\"<br \/>\n        class Assert          as \"NUnit.Framework.Assert\"<\/p>\n<p>        class cls-Account     as \"Account\"<br \/>\n        .<\/p>\n<p>       class-attributes.<br \/>\n           custom-attribute is cls-TestFixture.<\/p>\n<p>       static.<br \/>\n       data division.<br \/>\n       working-storage section.<br \/>\n       end static.<\/p>\n<p>       object.<br \/>\n       method-id. \"TransferFunds\" custom-attribute is cls-Test.<br \/>\n       local-storage section.<br \/>\n       01 src\tobject reference cls-Account.<br \/>\n       01 dest\tobject reference cls-Account.<br \/>\n       procedure division.<br \/>\n        set src to cls-Account::\"New\"<br \/>\n        invoke src::\"Deposit\"(200)<\/p>\n<p>        set dest to cls-Account::\"New\"<br \/>\n        invoke dest::\"Deposit\"(150)<\/p>\n<p>        invoke src::\"TransferFunds\"(dest, 100);<\/p>\n<p>        invoke Assert::\"AreEqual\"(250 as sys-single,dest::\"Balance\")<br \/>\n        invoke Assert::\"AreEqual\"(100 as sys-single,src::\"Balance\")<br \/>\n        exit method.<br \/>\n       end method \"TransferFunds\".<br \/>\n       end object.<br \/>\n       end program AccountTest.<\/p>\n<p><\/code><\/p>\n<p>The first thing to notice about this class is that it has a TestFixture attribute associated with it \u2013 this is the way to indicate that the class contains test code (this attribute can be inherited). The class has to be public and there are no restrictions on its superclass. The class also has to have a default constructor.<\/p>\n<p>The only method in the class \u2013 TransferFunds, has a Test attribute associated with it \u2013 this is an indication that it is a test method. Test methods have to return void and take no parameters. In our test method we do the usual initialization of the required test objects, execute the tested business method and check the state of the business objects. The Assert class defines a collection of methods used to check the post-conditions and in our example we use the AreEqual method to make sure that after the transfer both accounts have the correct balances (there are several overloadings of this method, the version that was used in this example has the following parameters : the first parameter is an expected value and the second parameter is the actual value).<\/p>\n<p>Compile and run this example. Assume that you have compiled your test code into a Example1.dll. Start the NUnit Gui (the installer will have created a shortcut on your desktop and in the \u201cProgram Files\u201d folder), after the GUI starts, select the File->Open menu item, navigate to the location of your bank.dll and select it in the \u201cOpen\u201d dialog box. When the bank.dll is loaded you will see a test tree structure in the left panel and a collection of status panels on the right. Click the Run button, the status bar and the TransferFunds node in the test tree turn red \u2013 our test has failed. The \u201cErrors and Failures\u201d panel displayed the following message \u2013 \u201cTransferFunds : expected <250> but was <150>\u201d and the stack trace panel right below it reported where in the test code the failure has occurred  \u201cat AccountTest.TransferFunds() in xxxxExample1Example1AccountTest.cbl:line 42\u201d<\/p>\n<p>That is expected behavior; the test has failed because we have not implemented the TransferFunds method yet. Now let\u2019s get it to work. Don\u2019t close the GUI and go back to your IDE and fix the code, make your TransferFunds method look like this:<\/p>\n<p><code lang=\"cobol\" width=\"700\" lines=\"-1\" nowrap=\"0\" >       method-id. \"TransferFunds\".<br \/>\n       local-storage section.<br \/>\n       linkage section.<br \/>\n       01 lnk-Account\tobject reference Account.<br \/>\n       01 lnk-amount    comp-2.<br \/>\n       procedure division using by value lnk-Account,<br \/>\n                                by value lnk-amount.<br \/>\n           invoke lnk-Account::\"Deposit\"(lnk-amount)<br \/>\n           invoke self::\"Withdraw\"(lnk-amount)<br \/>\n           exit method.<br \/>\n       end method \"TransferFunds\".<br \/>\n<\/code><\/p>\n<p>Now recompile your code and click the run button in GUI again \u2013 the status bar and the test tree turn green. (Note how the GUI has reloaded the assembly automatically for you; we will keep the GUI open all the time and continue working with our code in IDE and write more tests).<\/p>\n<p>Let\u2019s add some error checking to our Account code. We are adding the minimum balance requirement for the account to make sure that banks continue to make their money by charging your minimal overdraft protection fee. Let\u2019s add the minimum balance property to our Account class:<\/p>\n<p><code lang=\"cobol\" width=\"700\" lines=\"-1\" nowrap=\"0\" ><br \/>\n\t   01 minimumBalance\tcomp-2 value 10<br \/>\n\t\t\t\t\tproperty as \"MinimumBalance\".<br \/>\n<\/code><\/p>\n<p>We will use an exception to indicate an overdraft:<\/p>\n<p><code lang=\"cobol\" width=\"700\" lines=\"-1\" nowrap=\"0\" ><br \/>\n       class-id. InsufficientFundsException<br \/>\n                 as \"InsufficientFundsException\"<br \/>\n                 inherits cls-exception.<\/p>\n<p>       repository.<br \/>\n         class cls-exception as \"System.ApplicationException\".<br \/>\n       object.<br \/>\n       object-storage section.<br \/>\n       end object.<\/p>\n<p>       end class InsufficientFundsException.<br \/>\n<\/code><\/p>\n<p>Add two new classes reference to the repository:<\/p>\n<p><code lang=\"cobol\" width=\"700\" lines=\"-1\" nowrap=\"0\" ><br \/>\n     class ExpectedException<br \/>\n           as \"NUnit.Framework.ExpectedExceptionAttribute\"<br \/>\n     class InsufficientFundsException<br \/>\n           as \"InsufficientFundsException\"<br \/>\n<\/code><\/p>\n<p>Add a new test method to our AccountTest class:<\/p>\n<p><code lang=\"cobol\" width=\"700\" lines=\"-1\" nowrap=\"0\" ><br \/>\n     method-id. \"TransferWithInsufficientFunds\"<br \/>\n         custom-attribute is cls-Test<br \/>\n         custom-attribute is ExpectedException(<br \/>\n               type of InsufficientFundsException)<br \/>\n\t   .<br \/>\n       local-storage section.<br \/>\n       01 src\tobject reference cls-Account.<br \/>\n       01 dest\tobject reference cls-Account.<br \/>\n       procedure division.<br \/>\n        set src to cls-Account::\"New\"<br \/>\n        invoke src::\"Deposit\"(200)<\/p>\n<p>        set dest to cls-Account::\"New\"<br \/>\n        invoke dest::\"Deposit\"(150)<\/p>\n<p>        invoke src::\"TransferFunds\"(dest, 300);<br \/>\n        exit method.<br \/>\n       end method \"TransferWithInsufficientFunds\".<\/p>\n<p><\/code><\/p>\n<p>This test method in addition to Test attribute has an ExpectedException attribute associated with it \u2013 this is the way to indicate that the test code is expecting an exception of a certain type; if such an exception is not thrown during the execution \u2013 the test will fail. <\/p>\n<p>Compile your code and go back to the GUI. As you compiled your test code, the GUI has grayed out and collapsed the test tree as if the tests were not run yet (GUI watches for the changes made to the test assemblies and updates itself when the structure of the test tree has changed \u2013 e.g. new test is added). Click the \u201cRun\u201d button \u2013 we have a red status bar again. We got the following Failure : \u201cTransferWithInsufficentFunds : InsufficientFundsException was expected\u201d. Let\u2019s fix our Account code again, modify the TransferFunds method this way:\n<\/p>\n<p><code lang=\"cobol\" width=\"700\" lines=\"-1\" nowrap=\"0\" ><br \/>\n       method-id. \"TransferFunds\".<br \/>\n       local-storage section.<br \/>\n       linkage section.<br \/>\n       01 lnk-Account\tobject reference Account.<br \/>\n       01 lnk-amount    comp-2.<br \/>\n       procedure division using by value lnk-Account,<br \/>\n                                by value lnk-amount.<\/p>\n<p>           invoke lnk-Account::\"Deposit\"(lnk-amount)<br \/>\n           if balance - lnk-amount < minimumBalance\n             raise InsufficientFundsException::\"New\"()\n           end-if\n             \n           invoke self::\"Withdraw\"(lnk-amount)\n           exit method.           \n       end method \"TransferFunds\".      \n<\/code><\/p>\n<p>Compile and run the tests \u2013 green bar. Success! But wait, looking at the code we\u2019ve just written we can see that the bank may be loosing money on every unsuccessful funds Transfer operation. Let\u2019s write a test to confirm our suspicions. Add this test method:<\/p>\n<p><code lang=\"cobol\" width=\"700\" lines=\"-1\" nowrap=\"0\" ><br \/>\n       method-id. \"TransferWithInsufficientFundsAtomicity\"<br \/>\n               custom-attribute is cls-Test<br \/>\n       .<br \/>\n       local-storage section.<br \/>\n       01 src\tobject reference cls-Account.<br \/>\n       01 dest\tobject reference cls-Account.<br \/>\n       01 obj-InsufficientFundsException<br \/>\n          object reference InsufficientFundsException.<br \/>\n       procedure division.<br \/>\n        set src to cls-Account::\"New\"<br \/>\n        invoke src::\"Deposit\"(200)<\/p>\n<p>        set dest to cls-Account::\"New\"<br \/>\n        invoke dest::\"Deposit\"(150)<\/p>\n<p>        try<br \/>\n          invoke src::\"TransferFunds\"(dest, 300)<br \/>\n        catch obj-InsufficientFundsException<br \/>\n          continue<br \/>\n        end-try<\/p>\n<p>        invoke Assert::\"AreEqual\"(200 as sys-single,src::\"Balance\")<br \/>\n        invoke Assert::\"AreEqual\"(150 as sys-single,dest::\"Balance\")<\/p>\n<p>        exit method.<br \/>\n       end method \"TransferWithInsufficientFundsAtomicity\".<br \/>\n<\/code><\/p>\n<p>We are testing the transactional property of our business method \u2013 all operations are successful or none. Compile and run \u2013 red bar. OK, we\u2019ve made $300.00 out of a thin air (1999.com d\u00e9j\u00e0 vu?) \u2013 the source account has the correct balance of 150.00 but the destination account shows : $450.00. How do we fix this? Can we just move the minimum balance check call in front of the updates:<\/p>\n<p><code lang=\"cobol\" width=\"700\" lines=\"-1\" nowrap=\"0\" ><br \/>\n       method-id. \"TransferFunds\".<br \/>\n       local-storage section.<br \/>\n       linkage section.<br \/>\n       01 lnk-Account\tobject reference Account.<br \/>\n       01 lnk-amount    comp-2.<br \/>\n       procedure division using by value lnk-Account,<br \/>\n                                by value lnk-amount.<\/p>\n<p>           if balance - lnk-amount < minimumBalance\n             raise InsufficientFundsException::\"New\"()\n           end-if\n           \n           invoke lnk-Account::\"Deposit\"(lnk-amount)\n             \n           invoke self::\"Withdraw\"(lnk-amount)\n           exit method.           \n       end method \"TransferFunds\".    \n<\/code><\/p>\n<p>What if the Withdraw() method throws another exception? Should we execute a compensating transaction in the catch block or rely on our transaction manager to restore the state of the objects? We need to answer those questions at some point, but not now; but what do we do with the failing test in the meantime \u2013 remove it? A better way is to temporarily ignore it, add the following attribute to your test method<\/p>\n<p><code lang=\"cobol\" width=\"700\" lines=\"-1\" nowrap=\"0\" ><br \/>\n  method-id. \"TransferWithInsufficientFundsAtomicity\"<br \/>\n               custom-attribute is cls-Test<br \/>\n               custom-attribute is<br \/>\n  IgnoreTest(\"Need to decide how to implement transaction management in the application\")<br \/>\n<\/code><\/p>\n<p>Compile and run \u2013 yellow bar. Click on \u201cTests Not Run\u201d tab and you will see AccountTest.TransferWithInsufficientFundsAtomicity() in the list along with the Reason this test is ignored.<\/p>\n<p>Looking at our test code we can see that some refactoring is in order. All test methods share a common set of test objects. Let\u2019s extract this initialization code into a setup method and reuse it in all of our tests. The refactored version of our test class looks like this:<\/p>\n<p><code lang=\"cobol\" width=\"700\" lines=\"-1\" nowrap=\"0\" ><br \/>\n      $set preservecase sourceformat\"free\"<br \/>\n       class-id. AccountTest as \"AccountTest\".<\/p>\n<p>       environment division.<br \/>\n       configuration section.<br \/>\n       repository.<br \/>\n        class sys-single\tas \"System.Single\"<br \/>\n        class NUnit-TestFixture\t\t\t\tas<br \/>\n              \"NUnit.Framework.TestFixtureAttribute\"<br \/>\n        class NUnit-Test\t\t\t\t\tas<br \/>\n             \"NUnit.Framework.TestAttribute\"<br \/>\n        class NUnit-Setup\t\t\t\t\tas<br \/>\n             \"NUnit.Framework.SetUpAttribute\"<br \/>\n        class NUnit-Assert\t\t\t\t\tas<br \/>\n             \"NUnit.Framework.Assert\"<br \/>\n        class NUnit-IgnoreTest\t\t\t\tas<br \/>\n             \"NUnit.Framework.IgnoreAttribute\"<br \/>\n        class NUnit-ExpectedException\t\t      as<br \/>\n             \"NUnit.Framework.ExpectedExceptionAttribute\"<\/p>\n<p>        class cls-Account\t\t\t\t\tas<br \/>\n              \"Account\"<br \/>\n        class InsufficientFundsException\t            as<br \/>\n              \"InsufficientFundsException\"<br \/>\n        .<\/p>\n<p>       class-attributes.<br \/>\n           custom-attribute is NUnit-TestFixture.<\/p>\n<p>       object.<br \/>\n       working-storage section.<br \/>\n       01 src\tobject reference cls-Account.<br \/>\n       01 dest\tobject reference cls-Account.<\/p>\n<p>       method-id. \"Init\"<br \/>\n\t\tcustom-attribute is NUnit-Setup.<br \/>\n\t   procedure division.<br \/>\n           set src to cls-Account::\"New\"<br \/>\n           set dest to cls-Account::\"New\"<\/p>\n<p>           invoke src::\"Deposit\"(200)<br \/>\n           invoke dest::\"Deposit\"(150)<br \/>\n           exit method.<br \/>\n       end method \"Init\".<\/p>\n<p>       method-id. \"TransferFunds\"<br \/>\n\t\tcustom-attribute is NUnit-Test.<br \/>\n       procedure division.<br \/>\n        invoke src::\"TransferFunds\"(dest, 100);<br \/>\n        invoke NUnit-Assert::\"AreEqual\"(250,dest::\"Balance\")<br \/>\n        invoke NUnit-Assert::\"AreEqual\"(100,src::\"Balance\")<br \/>\n        exit method.<br \/>\n       end method \"TransferFunds\".<\/p>\n<p>       method-id. \"TransferWithInsufficientFunds\"<br \/>\n        custom-attribute is NUnit-Test<br \/>\n\t\tcustom-attribute is<br \/>\n              NUnit-ExpectedException(<br \/>\n               type of InsufficientFundsException).<br \/>\n       procedure division.<br \/>\n        invoke src::\"TransferFunds\"(dest, 300)<br \/>\n        exit method.<br \/>\n       end method \"TransferWithInsufficientFunds\".<\/p>\n<p>       method-id. \"TransferWithInsufficientFundsAtomicity\"<br \/>\n\t\tcustom-attribute is NUnit-Test<br \/>\n        custom-attribute is NUnit-IgnoreTest(<br \/>\n          \"Need to decide how to implement transaction management in the application\").<br \/>\n       local-storage section.<br \/>\n       01 obj-InsufficientFundsException<br \/>\n          object reference InsufficientFundsException.<br \/>\n       procedure division.<br \/>\n        try<br \/>\n          invoke src::\"TransferFunds\"(dest, 300)<br \/>\n        catch obj-InsufficientFundsException<br \/>\n          continue<br \/>\n        end-try<\/p>\n<p>        invoke NUnit-Assert::\"AreEqual\"(<br \/>\n              200 as sys-single,src::\"Balance\")<br \/>\n        invoke NUnit-Assert::\"AreEqual\"(<br \/>\n              150 as sys-single,dest::\"Balance\")<br \/>\n        exit method.<br \/>\n       end method \"TransferWithInsufficientFundsAtomicity\".  <\/p>\n<p>       end object.<br \/>\n       end program AccountTest.<\/p>\n<p><\/code><\/p>\n<p>Note that Init method has the common initialization code, it has void return type, no parameters, and it is marked with SetUp attribute. Compile and run \u2013 same yellow bar!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A while back, I spent a afternoon converting some C# NUnit documentation into COBOL .Net, so I thought I would share the document with the world, with the hope that it will help any one interested in using NUnit and &hellip; <a href=\"http:\/\/www.gennard.net\/blog\/2009\/11\/nunit-and-cobol-net\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,23,32],"tags":[209,220],"_links":{"self":[{"href":"http:\/\/www.gennard.net\/blog\/wp-json\/wp\/v2\/posts\/126"}],"collection":[{"href":"http:\/\/www.gennard.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.gennard.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.gennard.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.gennard.net\/blog\/wp-json\/wp\/v2\/comments?post=126"}],"version-history":[{"count":0,"href":"http:\/\/www.gennard.net\/blog\/wp-json\/wp\/v2\/posts\/126\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.gennard.net\/blog\/wp-json\/wp\/v2\/media?parent=126"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.gennard.net\/blog\/wp-json\/wp\/v2\/categories?post=126"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.gennard.net\/blog\/wp-json\/wp\/v2\/tags?post=126"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}