TDD in NAV – Test #2

Well, don't let us be distracted anymore. Our next test is awaiting us: Test #2.

#

Description Status

1a

Create PI (purchase invoice) with 1 line and check that total line amount is calculated right Complete

1b

Create PI with 1 line, populate line amount with random value and check that total line amount is calculated right Complete

2

Create PI with multiple (2) lines and check that total line amount is calculated right In Progress

3

Create PI with multiple lines and check that doc. amount verification succeeds

4

Create PI with multiple lines and check that doc. amount verification fails

Get on with it and …

1. Write our test code

We could do that from scratch, but let's do it efficient (sounds more professional than easy-going, or just plain lazy [:P]).

We already have our first test function PIwithOneLine in codeunit 60000 (Test Doc. Amount) and our second test function will have a lot in common and thus we will create a PIwithTwoLines test function as a copy of PIwithOneLine (you might recall one of my first blog entries on how to copy functions in NAV). Let's just show the code now:

PIwithTwoLines()

// Create a purchase header
PurchHeader.INSERT(TRUE);

// Create two purchase lines to the header
PurchLine."Document No." := PurchHeader."No.";
PurchLine."Line Amount" := 1;
PurchLine.INSERT;

PurchLine."Line Amount" := 2;
PurchLine.INSERT;

//Check if line amounts equals the line amounts calculated by CalcDocAmount
Assert.AreEqual(3,PurchHeader.CalcDocAmount,'Calc. Doc. Amount')

Minimal test code; no fuzz with variables to sum up the line amounts, hard coding suffices here.

2. Compile test code

… which shows GREEN!

Wow, this saves me to have to …

3. Implement just enough to compile

… and hence we can …

4. Run test and see it fail

Indeed, RED:

Did I hear you saying: "You could have known, Luc!"? And right you are: I could have known, but I just wanted to demonstrate that the whole setup is capable of showing me the way. Of course I cannot insert two times the same purchase line, i.e. two purchase lines having the same PK.

As such the code should have been:

PIwithTwoLines()

// Create a purchase header
PurchHeader.INSERT(TRUE);

// Create two purchase lines to the header
PurchLine."Document No." := PurchHeader."No.";
PurchLine."Line No." := 10000;
PurchLine."Line Amount" := 1;
PurchLine.INSERT;

PurchLine."Line No." := 20000;
PurchLine."Line Amount" := 2;
PurchLine.INSERT;

//Check if line amounts equals the line amounts calculated by CalcDocAmount
Assert.AreEqual(3,PurchHeader.CalcDocAmount,'Calc. Doc. Amount')

Let's update and rerun:

RED!

Makes sense? To me it does. The total of the lines is 3 (= 1 + 2), but my CalcDocAmount function (do you recall?) returns the "Line Amount"of the first line only. Here we are again to …

5. Implement just enough to make test pass

Open table 38 in design mode and modify the CalcDocAmount function:

CalcDocAmount() Amount : Decimal

PurchLine.SETRANGE("Document No.","No.");
  IF PurchLine.FINDFIRST THEN
    REPEAT
      TotLineAmount := TotLineAmount + PurchLine."Line Amount";
  UNTIL PurchLine.NEXT = 0;

EXIT(TotLineAmount)

Compile and ready to …

(BTW: did I already say that I did declare a variable TotLineAmount of data type Decimal?)

6. Run test and see it pass

… see it pass … [8] … RED/GREENRED/GREEN … [8][8] … see it pass? … [8][8][8] … RED/GREEN

Ready? Run codeunit 60000:

YES … GREEN! … [Y] … [{][}]

7. Refactor

Refactor? Anything to refactor? Looking at both the production and test code …?

CalcDocAmount seems quite OK now. What about our test functions? Have a look at this pattern:

PurchLine."Document No." := PurchHeader."No.";
PurchLine."Line Amount" := …;

PurchLine.INSERT;

Looks like we're hitting a duplication (or should we call it a triplication?). RE-FACT-OR! RE-FACT-OR! RE-FACT-OR! RE-FACT-OR! Can you hear the roaring drums?

OK, we'll refactor. Now relax, we will. [A]

1. (Re)write our test code (second time)

What about this?

PIwithOneLine()

// Create a purchase header
PurchHeader.INSERT(TRUE); //TRUE ensures that the system assigns a unique number to the header

// Create one purchase line to the header
CreatePurchLine(PurchHeader."No.",0,1);

//Check if line amount equals the line amount calculated by CalcDocAmount
Assert.AreEqual(1,PurchHeader.CalcDocAmount,'Calc. Doc. Amount')

PIwithTwoLines()

// Create a purchase header
PurchHeader.INSERT(TRUE);

// Create two purchase lines to the header
CreatePurchLine(PurchHeader."No.",10000,1);
CreatePurchLine(PurchHeader."No.",20000,2);

//Check if line amounts equals the line amounts calculated by CalcDocAmount
Assert.AreEqual(3,PurchHeader.CalcDocAmount,'Calc. Doc. Amount')

With a new function (having PurchLine as local variable referring to table 39):

CreatePurchLine(HeaderNo : Code[20];LineNo : Integer;Amount : Decimal)

PurchLine."Document No." := HeaderNo;
PurchLine."Line No." := LineNo;
PurchLine."Line Amount" := Amount;
PurchLine.INSERT;

Now …

2/3. Compile the test code (second time)

… which shows RED. RED? Yes, RED.

[:O]

Never seen that one before. Did you? But I guess that I know what this is about. Did I tell you that any function that we define in a test codeunit by default is of type (FunctionType) Test? Well, that's exactly what happened with our helper function CreatePurchLine:

And Test functions are not meant to be called to in code like Normal functions. So we change FunctionType to Normal, and also set Local to Yes.

Compile … GREEN … and …

4/5/6. Run the test and see it …

… pass! GREEN! Test #2 is completed:

#

Description Status

1a

Create PI (purchase invoice) with 1 line and check that total line amount is calculated right Complete

1b

Create PI with 1 line, populate line amount with random value and check that total line amount is calculated right Complete

2

Create PI with multiple (2) lines and check that total line amount is calculated right Complete

3

Create PI with multiple lines and check that doc. amount verification succeeds

4

Create PI with multiple lines and check that doc. amount verification fails

Note

… that, given previous results of our tests and therefor knowing that our production code is OK, refactoring the test code is a pretty save operation to do. [H]

TDD Series Index

  1. Test-Driven Development in NAV – Intro
  2. Test-Driven Development in NAV – Intro 2
  3. Test-Driven Development in NAV – By Example
  4. Test-Driven Development in NAV – Test #1
  5. TDD in NAV – Triangulation
  6. TDD in NAV – Test #2
  7. TDD in NAV – Test #3
  8. TDD in NAV – Test #4
  9. TDD in NAV – ASSERTERROR or IsFalse

Leave a Reply

Your email address will not be published. Required fields are marked *