Skip to the content

News

Business Central API – leave out CompanyId from the URL

Here’s a nice trick I wanted to share with you simply because I didn’t know this was possible (probably a lot of you already know, but if not – here you go ;-)). It could come in handy in some situations. You probably are very aware about how we’re supposed to use Business Central API’s …

Interview: Why test automation for Microsoft Dynamics 365 Business Central is a team effort

by , editor, MSDynamicsWorld.com

Last December the 2nd edition of Luc van Vugt’s book  was released by Packt Publishing. The book aims to explain how to “efficiently automate test cases for faster development with less time needed for manual testing.” And speed in development work is an urgent need for Business Central customers and their partners as they adopt Microsoft’s SaaS ERP model with its frequent updates and new requirements for alignment of custom extensions and ISV solutions.

Read the whole article on .

How Do I: Create a Calendar table in PowerBI?

In almost every PowerBI report at some point you will require time intelligence . You might want to display information on a timeline or create time based measures using DAX time intelligence functions. For that to work properly it’s a best practice...

Directions ASIA 2022 cancelled

Reality has hit and we can no longer put off the decision. Directions ASIA cannot happen for '22 and we will have to regroup in '23. You won’t be surprised to hear that its Covid that’s once again defeated our best endeavours to get partners, sponsors, and Microsoft face to face in Asia. We’ve tried, hard to make it happen with serial postponements. But without the ability of a ...

Configuring Business Central for Azure Active Directory authentication and OAuth (2)

After configuring Business Central on-premises for Azure Active Directory authentication, as explained in the previous blog post, it’s now time to configure it for OAuth authentication with APIs and web services. Two options There are two options that you can ...

Quick Tip: Quick Assist, free remote control tool in Windows

Did you know that Windows 10 came with a free remote control tool called Quick Assist ? Quick Assist is an app in Windows 10 that enables you to receive or provide assistance over a remote connection. Here’s a video about it: https://www.microsoft...

Quick Tip: New Powerpoint deck “Using telemetry to improve your partner practices and processes” available!

Yesterday Kennie Nybo Pontoppidan (@KennieNP) / Twitter announced on twitter that the new PowerPoint deck “ Using telemetry to improve your partner practices and processes ” is now available. You can find it here: BCTech/samples/AppInsights/Presentations...

Universal Code Initiative Program: Cloud-optimized extensions

Universal Code Initiative Program: Cloud-optimized extensions Approach to the initiative Write ‘Cloud’ target code – Cloud Ready Software In general,

Configuring Business Central for Azure Active Directory authentication and OAuth (1)

It’s a recurring question I get quite frequently: how to set up Business Central on-premises for Azure Active Directory authentication? And related to that, how to call APIs on Business Central on-premises with OAuth authentication? I know there are quite ...

From fresh to shared fixture #3 – refactoring can start

At the end of the of this series I left you hanging just when I was ready to really start refactoring. Sorry for that and shouldn’t let you no longer. So, here we go.

Refactoring can start

But where to start? Well, let’s list the things that could be cleaned up. In this I use the following rules of thumb:

  1. If data creation helper functions are referenced multiple times from different tests, these might be good candidates to be moved in to the Initialize function. In this way we promote fresh fixture  into shared fixture.
    Note that data creator helper functions are created based on the [GIVEN] clauses
  2. If different helper functions contain almost the same code, these are candidates for generalization: from a number of specific methods move the code into one generic method.

In the context of this blog post series we’ll focus on my first rule of thumb only.

So, what data creation helper methods in my test codeunit are referenced multiple times?

Have a look:

Having 5 tests coded and having two of the data creation helper functions referenced 5 times it’s quite obvious that these are called by each test function (and which we can easily check in the code). Sounds like real good candidates for a promotion to the Initialize function. If the concept of the Initialize function does not sound familiar to you read more about it in my book or in the .

// [GIVEN] Location with require shipment

I am first going to focus on the helper function CreateLocationWithRequireShipment for the very reason that it is applied in each of the 5 test functions in exactly the same way:

// [GIVEN] Location with require shipment
LocationCode := CreateLocationWithRequireShipment();

With LocationCode being a local variable of type Code[10].

Moving this statement into Initialize means that not each test will create a new location but instead will only be created once. This is not only has a refactoring result – elimination of duplication, or clean-up – but it also makes running all 5 tests more efficient as the location only needs to be created once; as we will see below.

Keep in mind: taking small steps. Let’s not refactor all 5 tests in one go but one by one running the tests after each (small) change.

Before – [SCENARIO #0001]

[Test]
procedure DeleteByUserWithNoAllowanceManuallyCreatedWhseShptLine()
// [FEATURE] Unblock Deletion of Whse. Shpt. Line enabled
var
    LocationCode: Code[10];
    ...
begin
    // [SCENARIO #0001] Delete by user with no allowance manually created whse. shpt. line
    Initialize();

    ...
    // [GIVEN] Location with require shipment
    LocationCode := CreateLocationWithRequireShipment();
    ...
end;

The three dots (…) are used to replace other codes parts that are not in focus right now.

After – [SCENARIO #0001]

[Test]
procedure DeleteByUserWithNoAllowanceManuallyCreatedWhseShptLine()
// [FEATURE] Unblock Deletion of Whse. Shpt. Line enabled
var
    ...
begin
    // [SCENARIO #0001] Delete by user with no allowance manually created whse. shpt. line
    // [GIVEN] Location with require shipment
    Initialize();

    ...
end;

Before – Initialize

local procedure Initialize()
begin
    if IsInitialized then
        exit;

    IsInitialized := true;
    Commit();
end;

The make the focus on our work here better I have simplified the setup of Initialize here compared to the one you will find in the GitHub repo.

After – Initialize

var
    LocationCode: Code[10];

local procedure Initialize()
begin
    if IsInitialized then
        exit;

    // [GIVEN] Location with require shipment
    LocationCode := CreateLocationWithRequireShipment();

    IsInitialized := true;
    Commit();
end;

Now that LocationCode is assigned a value in Initialize and is used in the first test function, it needs to be declared as a global variable as can been seen above.

Exercising the same refactoring step for the other four tests – refactoring and retesting one by one – will give the following final test run result:

Notes

  • Refactoring any of the next test functions is with a smaller step than for the first test function as I do not need to update the Initialize function.
  • After each rerun the duration of that test diminishes… except for the first test, because with that test Initialize is fully executed.
  • Running each test individually will make it duration again just as long as before, because in the case the location needs to be created.
  • We have to realize that we can “promote” CreateLocationWithRequireShipment due to the fact that the location is not consumed, being that, it indeed is reusable in all those five tests.

// [GIVEN] Enable “Unblock Deletion of Shpt. Line” on warehouse setup

What next? Well, the other data creator EnableUnblockDeletionOfShptLineOnWarehouseSetup.This is called in exactly the same way in the first four tests. These have all a similar setup/purpose, so, we could most probably move this into the Initialize function.

Before – [SCENARIO #0001]

[Test]
procedure DeleteByUserWithNoAllowanceManuallyCreatedWhseShptLine()
// [FEATURE] Unblock Deletion of Whse. Shpt. Line enabled
var
    ...
begin
    // [SCENARIO #0001] Delete by user with no allowance manually created whse. shpt. line
    // [GIVEN] Location with require shipment
    Initialize();

    ...
    // [GIVEN] Enable "Unblock Deletion of Shpt. Line" on warehouse setup
    EnableUnblockDeletionOfShptLineOnWarehouseSetup();
    ...
end;

The three dots (…) are used to replace other codes parts that are not in focus right now.

After – [SCENARIO #0001]

[Test]
procedure DeleteByUserWithNoAllowanceManuallyCreatedWhseShptLine()
// [FEATURE] Unblock Deletion of Whse. Shpt. Line enabled
var
    ...
begin
    // [SCENARIO #0001] Delete by user with no allowance manually created whse. shpt. line
    // [GIVEN] Enable "Unblock Deletion of Shpt. Line" on warehouse setup
    // [GIVEN] Location with require shipment
    Initialize();

    ...
end;

Before – Initialize

local procedure Initialize()
begin
    if IsInitialized then
        exit;

    // [GIVEN] Location with require shipment
    LocationCode := CreateLocationWithRequireShipment();

    IsInitialized := true;
    Commit();
end;

Note that the setup of Initialize here is somewhat simpler than you will find in the GitHub repo.

After – Initialize

local procedure Initialize()
begin
    if IsInitialized then
        exit;

    // [GIVEN] Enable "Unblock Deletion of Shpt. Line" on warehouse setup
    EnableUnblockDeletionOfShptLineOnWarehouseSetup();
    // [GIVEN] Location with require shipment
    LocationCode := CreateLocationWithRequireShipment();

    IsInitialized := true;
    Commit();
end;

But what about the fifth test in which EnableUnblockDeletionOfShptLineOnWarehouseSetup is not used in the [GIVEN] section, rather in the [WHEN]? If we would add this helper function to Initialize will it be triggered uselessly? As matter of fact, not really, as it is already triggered in each preceding test anyway.

Let’s see what the overall effect is on the test duration:

Notes

  • Having EnableUnblockDeletionOfShptLineOnWarehouseSetup executed uselessly
  • Of course, as we all know each process, like a test run in our case, is not executing in exactly the same duration each time. The above screenshot is just one example of a test run.
  • The duration shortening due to the promotion of EnableUnblockDeletionOfShptLineOnWarehouseSetup to Initialize is nothing compared to the promotion of CreateLocationWithRequireShipment .