Skip to the content

News

Microsoft’s solution for COVID-19 is a free Teams subscription for six months

Hi Guys, yes, I live in a “Red Zone”…it is certainly not a good situation, it takes a lot of

Using the power of Queries to bring some Smartness into Business Central

Currently there’s a lot of talk about Business Central performance . Microsoft has released and is releasing guidelines, tools about mechanisms to program for performance, analyse and monitor  performance. At the same moment there’s also discussions...

Test Tool Extension

When working on getting the standard tests running on our solution the standard Test Tool was not making our live as easy as it could. Wanting to rerun only successful or failing tests, this was near to impossible. Manually deselecting individual tests turning of the Run field, was, euphemistically said, not the most meaningful thing to do. So, already a couple of years ago we decided to add some actions to the test tool allowing us to (1) select the Run field for (a) all tests (b) only failing tests and thus disabling all others (c) non-failing tests, thus disabling failing tests and next to that (2) deselect the Run field for all tests, thus disabling all tests. It was practically implemented in the C/SIDE objects and not very hooligan proof, I have to admit. When writing my last year, I decided to rework this feature into an extension using this refactor project at the same time as a next exercise for myself on writing automated tests. The code has ever since then been available on GitHub and has been called . As it was never advertised outside of the book, so far, probably not a lot of people have been making use of it.

Test Tool Extension features

The main purpose of this project was to share with you its simple and powerful features, allowing you, as already noted above, to select and deselect specific tests. Thus enabling you to only run relevant test for your current task. For this four actions have are added to the Actions ribbon tab:

  1. Select on All
  2. Deselect on All
  3. Select on Failures
  4. Select on Non Failures

And all of these actions respect the active filter on the lines of the Test Tool. With the first action you make sure all tests (within the filter settings) are enabled. The second is its complement and does disable all tests within the filter. The third action will enable only the failing tests, where the fourth action will makes sure all others, being successful and skipped test, are only enabled. Of course when no filters are set all actions will apply to all tests in the active test suite.

The following screenshots show these actions in both Windows and Web Client:

Next to these four actions a very simple, but o, so, useful other addition has been made to the Test Tool: the Ctrl+F9 short cut to the Run Selected action (which unfortunately will not work on the Web Client). If you are working a lot on tests running them from the Test Tool, you probably will not often use the Run action, but rather Run Selected, as the latter will not throw up a dialog asking you what you want to run, but instead will immediately run the select test function or test codeunit.

Enough about the features. Let's discuss the two other purposes of this project:

  • automated test examples
  • ATDD test script examples

Automated test examples

In the folder  you will find 4 test codeunits containing all together 56 (unit) tests that do test all the features. Being an advocate of the test scripting, all tests have been designed and structured in this manner. Use them as a next collection of examples (next to my book) of how I think test codeunits/functions should be organized. Readable at high level and making use of reusuable helper functions at local (specific) and library (generic) level. The 3 library codeunits can be found in and containing all kinds of reusable relevant creator, getter, setter, and verifier functions.

ATDD test script examples

With my book I introduced a 4-steps recipe helping me to effectively code new tests, given ATDD scripted tests:

  1. Create a test codeunit
    • with name based on [FEATURE] tag
  2. Embed the customer wish into a test function
    • with name based upon [SCENARIO] tag
  3. Write your test story
    • based upon [GIVEN], [WHEN], and [THEN] tags
  4. Construct your real code

The book also discusses how to practice ATDD test scripting and do this efficiently use a Excel based setup, that I just started developing around that time. Both the 4-steps recipe and the Excel sheet having repeatable were the forerunner to what Jan Hoek and I started to conceive: . For those who have never heard of it, get yourself update with this of July 16, 2019.

All test in Test-Tool-Extension project have actually been conceived using ATDD test scripts that were converted to basic test codeunits using the ATDD.TestScriptor. Here's on example of the basic script in PowerShell making use of the ATDD.TestScriptor syntax, followed by its AL counter part created with the ATDD-TestScriptor comdlet ConvertTo-ALTestCodeunit:

Feature 'Enabling Actions on Failures Unfiltered' {
    Scenario 1 'Unfiltered disabled failed test functions' {
        Given	'One disabled test codeunit with five disabled failed test functions'
        Given	'One disabled test codeunit with three disabled failed test functions'
        When	'Do nothing'
        Then	'Select on All is enabled'
        Then	'Deselect on All is disabled'
        Then	'Select on Failures is enabled'
        Then	'Select on Non Failures is disabled'
    }
}

codeunit 92052 "Enabl. Actions on Failures FLX"
{
    // (c) fluxxus.nl - https://github.com/fluxxus-nl/Test-Tool-Extension

    // [FEATURE] Enabling Actions on Failures Unfiltered
    SubType = Test;

    [Test]
    procedure UnfilteredDisabledFailedTestFunctions()
    // [FEATURE] Enabling Actions on Failures Unfiltered
    begin
        // [SCENARIO #0001] Unfiltered disabled failed test functions
        Initialize();

        // [GIVEN] One disabled test codeunit with five disabled failed test functions
        CreateOneDisabledTestCodeunitWithFiveDisabledFailedTestFunctions();
        // [GIVEN] One disabled test codeunit with three disabled failed test functions
        CreateOneDisabledTestCodeunitWithThreeDisabledFailedTestFunctions();

        // [WHEN] Do nothing
        DoNothing();

        // [THEN] Select on All is enabled
        VerifySelectOnAllIsEnabled(NoFilterOnCodeunit());
        // [THEN] Deselect on All is disabled
        VerifyDeselectOnAllIsDisabled(NoFilterOnCodeunit());
        // [THEN] Select on Failures is enabled
        VerifySelectOnFailuresIsEnabled(NoFilterOnCodeunit());
        // [THEN] Select on Non Failures is disabled
        VerifySelectOnNonFailuresIsDisabled(NoFilterOnCodeunit());
    end;
}

Find the ATDD test script sources in the folder .

Feel free to make use of the extension and its code. Please, share any suggestions for improvements be means of issues.

Object types is case sensitive in TenantWebServiceCollection

Last week while I was working on an integration project, I wanted to call a Business Central function from Azure Logic Apps. I had few options with APIs but I went ahead with creating a CodeUnit with...()

Test Fixture Initializer

Overtime I have been advocating the use of the standard Business Central tests in various posts, webinars, conference presentations, and my . IMHO this has been, and still, is an undervalued collection of over 22,000 automated tests that cover all functional areas. It's a no brainer to let them run on your solution as . In case your solution intertwines with standard code chances are big that these tests will also hit your code resulting in a number of failing tests. As discussed in this this run resulted in over 75 % of failing tests when executed on our solution. Meaning that indeed the standard tests did hit our code and that we had a potential horizon of thousands of tests that could be running on our solution. After 6 weeks of work we had, on our NAV 2016 solution, a test collateral of more than 14,000 tests that was going to be run every night. A test run that each morning was going to show us the sanity of that part of our solution. Today, on NAV 2018, this collateral entails 18,300 tests, mainly standard. But we have meanwhile also been writing our own.

By mainly working on the of the tests we could raise the success rate from an initial 23 % to 72 % within one week of work. And eventually even more. Based on this work and some suggestions we also got a buy-in from Microsoft in improving their test code by adding publishers that would allow us to adjust standard tests with little to none footprint. The most import improvement was implemented by Microsoft in the Initialize function that the majority of standard tests hold to take care of the shared fixture or generic fresh fixture:

local procedure Initialize()
begin
    // Generic Fresh Setup
    LibraryTestInitialize.OnTestInitialize();
    //

    // Lazy Setup
    if isInitialized then
        exit();
    LibraryTestInitialize.OnBeforeTestSuiteInitialize();
    //

    isInitialized := true;
    Commit();
    LibraryTestInitialize.OnAfterTestSuiteInitialize();
end;

Using the now available publishers we built up code that I have now published on and have called the Test Fixture Initializer. To enable the creation of a shared fixture we subscribe to the OnBeforeTestSuiteInitialize publisher as it has proven to do this before any standard code does. To achieve the same for a generic fresh fixture we subscribe to the OnTestInitialize publisher. The OnAfterTestSuiteInitialize publisher will fire after standard code has done its part on he shared fixture. We have experienced that we hardly ever use this one, but of course you might if your code needs extend the shared fixture after everything else and/or needs to happen after the Commit.

The following schema shows how the code is structured:

OnTestInitialize

At this level you will be adding code for each specific standard test codeunit in SetGenericFreshSetup in Codeunit 97004 (FLX Generic Fresh Setup) to get generic fresh fixture created:

Codeunit 97004 "FLX Generic Fresh Setup"
{
    // (c) fluxxus.nl

    procedure SetGenericFreshSetup(CallerCodeunitID: Integer)
    begin
        case CallerCodeunitID of
            Codeunit::"ERM Sales Batch Posting": // please be sure only to remove this example codeunit if not relevant to you
                SetGenericFreshSetupForERMSalesBatchPosting();
        // add specific codeunits and your method to set generic fresh fixture here
        end
    end;

    local procedure SetGenericFreshSetupForERMSalesBatchPosting()
    begin
    end;
}

OnBeforeTestSuiteInitialize

At this level you will be adding each specific standard test codeunit to the case statement in  DoesCodeunitNeedLazySetup function in Codeunit 97005 (FLX Check Lazy Setup) to get standard shared fixture created for it by the Initialize function in Codeunit 97000 (FLX Initialize):

Codeunit 97005 "FLX Check Lazy Setup"
{
    // (c) fluxxus.nl

    procedure DoesCodeunitNeedLazySetup(CallerCodeunitID: Integer): Boolean
    begin
        case CallerCodeunitID of
            // add specific codeunits here that need additonal shared fixture
            Codeunit::"Purch. Document Posting Errors": // please be sure only to remove this example codeunit if not relevant to you
                exit(true);
            else
                exit(false);
        end;
    end;
}

If a standard codeunit needs some specific, i.e. additional, shared fixture add the codeunit to the case statement in SetAdditionalLazySetupBefore in Codeunit 97007 (FLX Additional Lazy Setup):

Codeunit 97007 "FLX Additional Lazy Setup"
{
    // (c) fluxxus.nl

    procedure SetAdditionalLazySetupBefore(CallerCodeunitID: Integer)
    begin
        case CallerCodeunitID of
            Codeunit::"ERM Edit Posting Groups": // please be sure only to remove this example codeunit if not relevant to you
                SetAdditonalLazySetupBeforeForERMEditPostingGroups();
        // add specific codeunits and your method to set additonal shared fixture here
        end
    end;

    local procedure SetAdditonalLazySetupBeforeForERMEditPostingGroups()
    begin
    end;
}

Initialize

The spill of the Test Fixture Initializer is the Initialize function in Codeunit 97000 (FLX Initialize). This is were you will need to add various functions that do create the data (or update existing CRONUS data). The next code example is an excerpt from the Initialize for our solution:

procedure Initialize()
begin
    if not DoesPrebuiltFixtureExist thn
    begin
        SetPrebuiltFixtureExists(true);

        N4LibraryLabel.CreateLabels;
        N4LibrarySetup.CreateTNTSetups;
        N4LibrarySetup.SetCompanyInformation;
        N4LibrarySetup.SetWarehouseSetup;
        N4LibrarySetup.SetInventorySetup;
        N4LibraryERM.CreateVATPostingSetups;
        N4LibraryERM.UpdateVATProdPostingGroups;
        N4LibrarySetup.SetPurchaseSetup;
        N4LibrarySetup.CreateSalesSetups;
        N4LibrarySetup.SetSalesSetups;
        N4LibrarySetup.CreateVDESetups;
        N4LibrarySetup.CreateOWSSetups;
        N4LibrarySetup.CreateOWSCommunicationSetups;
        N4LibrarySetup.CreateCBSetups;
        N4LibrarySetup.CreateOWSNumbersSetups;
        N4LibrarySetup.CreateMultiple3SNoSeries;
        N4LibrarySetup.CreateTransactionModes;
        N4LibrarySetup.CreateMultiplePaymentTerms;
        N4LibrarySetup.CreateTermPeriodDefinitions;
        N4LibrarySetup.CreateOWSSelections;
        N4LibrarySetup.CreateReqWorksheetTemplates;
        N4LibrarySetup.UpdateCustomerTemplates('STS');
        N4LibrarySetup.UpdateCustomers('STS');
        N4LibrarySetup.CreateDeliverabilityCodes;
        N4LibrarySetup.UpdateSalesDocuments('STS');
        N4LibrarySetup.UpdateItems;
    end;
end;

Initialize Prebuilt Fixture

As some of you probably know I am a huge fan of not wanting to have a prebuilt fixture. I want my test code to control and create the fixture needed so it is independent of the setting it will run in. Nevertheless, as you can see the Test Fixture Initializer project has enabled with Codeunit 97008 (FLX Initialize Preb. Fixture) to run the Initialize function in Codeunit 97000 (FLX Initialize). This is what we do before a newly created test suite will be run, as this shortens the total time of the test run. For that reason the Test Setup table has been added to the project, so that Prebuilt Fixture Exists field  can be check marked once the prebuilt fixture has been created. Any time the Initialize function is to be called this field will be verified whether or not the prebuilt fixture already exists.

Hope this all makes sense and will help in getting the standard test added to your test collateral. Feel free to make use of the code, and share your suggestions for improvements be means of an issue report.

DevOps Build Agents for Microsoft Dynamics 365 Business Central

Not too long ago, I did a webinar for ALOps.  The idea of these webinars is simple: how can you get started with DevOps for Microsoft Dynamics 365 Business Central.  And I’m not going to lie: I will focus on doing that with ALOps.  But that’s not the only focus of these videos – I …

Directions ASIA Postponed

The Directions 4 Partners Committee has been continuously monitoring the coronavirus (COVID-19) outbreak in relation to Directions ASIA, April 2-3, in Bangkok. The health and safety of our community is and always will be our main priority. That's why we have decided to reschedule Directions ASIA 2020 to the second half of the year.  We are still working very hard on confirming new dates at the same venue and will communicate them shortly. 

“How-to” use the restored Business Central SaaS database on On-premise environment – Part II

“How-to” use the restored Business Central SaaS database on On-premise environment – Part II Some have written to me about

Searching the unsearchable, episode 2

True, there was no episode 1, not on this blog. But if Star Wars can start with episode 4, I don’t see why can’t I start with episode 2. Especially when Waldo has already written…

The post appeared first on .

Codeunit API’s in Business Central

This blog post was on my list way too long… But now I found some time to sit down and write it. Disclaimer What I’m going to show here is officially not supported (yet). It is an undocumented feature that ...