Seeing as I’m on a bit of a run of posts about testing, let’s look at it from a slightly different angle.
Testing the Test
If we’re going to rely on automated tests to verify that our code (still) works then we need to have confidence that the tests themselves actually work.
Writing the Test First
This is why it is helpful to write and run the tests first. When you start developing a new feature or working on a bug fix you have identified some desired behaviour that the system doesn’t yet exhibit. Given this and this, when something or other then this is the behaviour I’m expecting.
Writing a test for that behaviour and seeing it fail confirms that the desired behaviour is missing. That gives you some confidence that you’re on the right lines – the system should do this, but doesn’t – yet.
When you write the bug fix or new feature and see the test pass it gives you much more confidence that your code actually works. You demonstrated beforehand that the desired behaviour was missing and that now it is there. Have a gold star.
Writing the Test Afterwards
You could write the test afterwards and we’ve done a lot of that as we’ve built up tests for our older code that didn’t have any. Whenever I write tests after the fact I do miss the initial stage of having an expected failing test though.
Not completing the given or the when can be a useful way to test the test. Asserting the expected results when you haven’t done all the required steps should normally cause the test to fail.
//[GIVEN] an item with my bespoke field populated LibraryInventory.CreateItem(Item); SomeBespokeValue := ...; //leave these lines commented out initially to see the test fail //Item.Validate("Bespoke Field",SomeBespokeValue); //Item.Modify(true); //[WHEN] the item is validated on a sales line LibrarySales.CreateSalesDocumentWithItem(...) //[THEN] some bespoke field on the sales line should be set Assert.AreEqual(SalesLine."Bespoke Field",SomeBespokeValue,...)
Seeing the test fail with those lines commented out and then seeing it pass when you uncomment them will give you more confidence that the test and the behaviour that it is testing work as required. If you are testing code that you think already works and the test always passes it is hard to be sure why the test is passing. Hopefully because the code works – but possibly because the test itself is broken and will always pass, even if the code doesn’t work.
The point is to try and get some confidence in your test results. Are you happy to ship the software when all your tests pass? If not, why not? Because you don’t have enough tests? Because you don’t trust that a passing test means working software?
Having a bunch of tests whose results you don’t trust is probably worse than having no tests at all.
This is all pretty generic and if you’re interested in the principles you can search for Test-Driven Development (TDD) or Behaviour-Driven Development (BDD) and read what people far more qualified than me have to say about it.
Let’s talk about Business Central specifically for a minute. One of the best things about automated testing compared to manual testing is that everything is rolled back at the end to return the database to the same state it was in at the start. However, that can make life a little difficult when you are trying to inspect the data mid-test and see what is happened.
There are various ways you might want to extract the data at a given moment: write to a file, throw an error with a bunch of values you are interested in, read uncommitted data in SQL. We’ll just talk about two approaches:
You can debug test code just like any other code. Set a breakpoint in your test, attach the debugger and run the test from the Test Tool page. Step through, add watches and evaluate debug expressions. The debugger in VS Code is getting better all the time, exposing more details about the variables you are interested in and SQL statements that have been executed.
Perfect for diving into the details and stepping through line by line, but not always the easiest to get an overview of what is happening.
Another Client Session
Another option is to open another client session while you are debugging. Set a breakpoint, attach the debugger and start running a test from the Test Tool page.
Debugging the test will block the session that you started it from – you’ll get the “working on it” dialog – but you can open a different session in another tab or in another browser.
The only snag with this is that some of the records that you want to read might be locked and you’ll get an error trying to open the corresponding page.
“The operation could not complete because a record was locked by another user.” Bummer.
Turns out there is another way to read the data in that session.
Avoid Locks With Page=<pageid>
You can add parameters to the web client URL to navigate to specific tables, reports or pages. In my example I can’t open the Items list from the menu because the record is locked by another user.
If I go to the Item List page with http://<base web client URL>?page=32 then the page loads with my test data. I can open the item card, navigate to other pages and run the Page Inspector (Ctrl+Alt+F1) to view all the fields in the table, filters, extension details etc.
As I step through the code in the VS Code debugger I can refresh the pages in this session and see the updates to the record. Beautiful.
If you’re interested in getting stuck into testing in Business Central grab yourself a copy of Automated Testing in Microsoft Dynamics 365 Business Central.
It was my pleasure to make a small contribution to this book as a technical reviewer and writing the foreword.