It’s been a while that I wrote on NAV and automated tests. In the meanwhile I have been teaching a lot all over Europe, and, yes, advocating test automation in NAV. And what did you do since my last post How-to: Run Standard Tests against Your Code? Did you dare and try? And did you also have the time/guts to continue with it? I know some that did.
Meanwhile I succeeded to get MS listen and implement a major request I had posed already some time ago. We’ll get to that below as it relates to the topic I would like to talk about here: Test Fixture.
If you have been testing software, and probably you do being in my audience, you know that a major part of your testing effort is getting data prepared to allow you to execute your tests. Having been a tester I dare to claim it is THE major part. Test Fixture is the general term for the setup up your system before you execute your test(s), be it software or hardware.
With respect to NAV test automation I have adopted the three level approach Bas Graaf used in his NAV Tech Days presentation ages ago, which he based on xUnit Patterns:
- Prebuilt Fixture
- Lazy Setup or Shared Fixture
- Fresh Setup or Fresh Fixture
Applying this to NAV:
- Prebuilt Fixture is the database we will run our tests on.
Typically for the standard Test Toolkit this is CRONUS, containing all sorts of preset data.
- Lazy Setup or Shared Fixture is the data created at the start of a related number of tests.
For those who have been looking into these, this is what the Initialize function takes care of in each test codeunit, and typically is about master, supplemental and setup data.
- Fresh Setup or Fresh Fixture is data created for a specific test only.
So this is data added to the shared fixture needed to execute a specific test. This often entails transactional data and custom master data.
When I started the project to get the Dynamics NAV Test Toolkit run on our code as per How-to: Run Standard Tests against Your Code, it was giving this result:
23 % percent of a total of 16.124 NAV 2016 tests where successful. Using a statistical approach, tackling the most occurring issues first, I could raise this to 72 % in less than a week. And mainly by focusing on the lazy setup:
By creating one generic Initialize function of our own:
Initialize() IF NOT IsInitialized THEN BEGIN IsInitialized := TRUE; N4LibraryLabel.CreateLabels; N4LibrarySetup.CreateTNTSetups; N4LibrarySetup.SetCompanyInformation; N4LibrarySetup.SetWarehouseSetup; N4LibrarySetup.SetInventorySetup; N4LibraryERM.CreateVATPostingSetups; N4LibraryERM.UpdateVATProdPostingGroups; N4LibrarySetup.SetPurchaseSetup; N4LibrarySetup.CreateSalesSetups; N4LibrarySetup.CreateVDESetups; N4LibrarySetup.CreateOWSSetups; N4LibrarySetup.CreateOWSCommunicationSetups; N4LibrarySetup.CreateCBSetups; N4LibrarySetup.CreateOWSNumbersSetups; N4LibrarySetup.CreateMultiple3SNoSeries; N4LibrarySetup.CreateTransactionModes; N4LibrarySetup.CreateMultiplePaymentTerms; N4LibrarySetup.CreateTermPeriodDefinitions; N4LibrarySetup.CreateOWSSelections; N4LibrarySetup.CreateReqWorksheetTemplates; N4LibrarySetup.CreateDeliverabilityCodes; N4LibrarySetup.UpdateItems; END;
… that I hooked into the local Initialize function of each test codeunits, like in the following example in COD134126 (ERM Reversal VAT Entries):
LOCAL Initialize() LibrarySetupStorage.Restore; IF IsInitialized THEN EXIT; //<<< N4LibraryInitialize.Initialize; //>>> LibraryERMCountryData.CreateVATData; LibraryERMCountryData.UpdateGeneralLedgerSetup; LibraryERMCountryData.UpdateGeneralPostingSetup; LibraryERMCountryData.UpdateVATPostingSetup; IsInitialized := TRUE; COMMIT; LibrarySetupStorage.Save(DATABASE::"General Ledger Setup");
What the h*ck, lazy setup? Sho-go-nai as the Japanese say, as I learned from Mark Brummel: Nothing can be done about it. Yes, I had to modify 480 test codeunits (of the total of 628) to get this working! And I told MS that I had to. This could have been prevented by a better design of there Initialize function from the start. And yessss, MS listened and now with NAV 2018 CU3 a redesign has been implemented in the first 100 test codeunits:
LOCAL Initialize() //<<< LibraryTestInitialize.OnTestInitialize(CODEUNIT::"ERM Apply Sales/Receivables"); //>>> LibraryVariableStorage.Clear; LibrarySetupStorage.Restore; IF isInitialized THEN EXIT; //<<< LibraryTestInitialize.OnBeforeTestSuiteInitialize(CODEUNIT::"ERM Apply Sales/Receivables"); //>>> LibraryERMCountryData.CreateVATData; LibraryERMCountryData.UpdateAccountInCustomerPostingGroup; LibraryERMCountryData.UpdateGeneralLedgerSetup; LibraryERMCountryData.UpdateGeneralPostingSetup; ModifyGenJnlBatchNoSeries; isInitialized := TRUE; COMMIT; LibrarySetupStorage.Save(DATABASE::"General Ledger Setup"); //<<< LibraryTestInitialize.OnAfterTestSuiteInitialize(CODEUNIT::"ERM Apply Sales/Receivables"); //>>>
where LibraryTestInitialize is COD132250 (Library – Test Initialize) and contains these three event publisher to which we now can subscribe:
[External] [IntegrationEvent] OnTestInitialize(CallerCodeunitID : Integer) [External] [IntegrationEvent] OnBeforeTestSuiteInitialize(CallerCodeunitID : Integer) [External] [IntegrationEvent] OnAfterTestSuiteInitialize(CallerCodeunitID : Integer)
So getting your lazy setup done on standard tests is a no-brainer. Another threshold taken to get test automation going.