automated testing in dynamics 365 for operations
TRANSCRIPT
Testing Quadrants
Source: Brian Marick
Performance & Load Testing
Security Testing
Tools
Exploratory Testing
Usability Testing
Manual
UAT
Automated
& Manual
Functional Testing
Story Tests
Business Cycle or Integration
Automated
Unit Tests
Component Tests
Automated test best practices
Reliable
Tests produce
the same
results run after
run
Maintainable
Minimal or no
effort to update
from version to
version
Fast
Supports
developer
testing and
gated check-in
Simple
Tests have a
clear intent and
are easy to
understand
Precise
Failures are
localized, easy
to pinpoint
exactly why the
test failed
Unit test best practices• Focus on what you’re testing and target that
• Don’t overload the purpose of the test
• Only test one thing in each test method
• Test both positive and negative outcomes, and important edge cases
• You may end up with many unit tests for a single method
• Be explicit on granularity of the test using the attribute [SysTestGranularityAttribute::Unit)]
• Write valuable unit tests - don’t test un-customized framework elements, focus on “brittle” areas of code
• Consider writing the unit test before you code the method or bug fix - this will ensure you have a better understanding about what you are developing
Why automated tests?
• Safety net for new developers
• Reduce the risk of “cracking open” complicated modules
• Reduce the burden on manual (human) regression testing
• Significantly increase product quality
• Potential to increase code quality “for free” via gated check-ins
• Little or no lag time between check-in and test break
Test Pyramid in D365FO
Unit
Component
Integration
Business Cycle
Or System
Business CycleTask Recorder
IntegrationTask Recorder / SysTest
ComponentTask Recorder / SysTest
UnitSysTest
Data Rollback• Baked into SysTest framework
• This is NOT x++ transactions – Uses SQL SavePoints
• Transactions are nested• Framework > TestSuite > TestClass > TestMethod
• Eliminates need for developer to manage data rollback, increase test reliability
• Coding errors or exceptions no longer leave data in an unreliable state
Data Rollback
Test
Framework
SavePoint
ManagerSuite
Create SavePoint 1(Suite)
Rollback to SavePoint 2(Class)
Suite setup()
Test
Class A
Test
Class B
Create SavePoint 2(Class)
SetupTestCase()
setup()
test1()
teardown()
setup()
test2()
teardown()
Rollback to SavePoint 2(Class)
Rollback to SavePoint 1(Suite)
Create SavePoint 3(Class)
Rollback to SavePoint 3(Class)
setup()
test1()
teardown()
setup()
test2()
teardown()
Rollback to SavePoint 3(Class)
Rollback to SavePoint 1(Suite)
Suite teardown()
Rollback to Initial
SetupTestCase()
Anatomy of a Unit Test[SysTestTarget(classStr(PCLPCFCostCodeManager), UtilElementType::Class), SysTestFixture(classStr(PCLPCFCostCodeTestSuite))] class PCLPCFCostCodeManagerTests extends SysTestCase { [SysTestMethodAttribute, SysTestGranularityAttribute(SysTestGranularity::Unit)] public void test_Component_Level_3_Cost_Code_Cannot_Be_Deleted() { //Arrange PCLPCFProjCostHierarchyTreeNode costCode; costCode.CostCode = PCLPCFCostCodeTestSuite::TestCostCodeNumber; costCode.CostLevel = PCLPCFCostLevel::Component; // skip insert validation logic, irrelevant to the test costCode.doInsert(); //Act boolean result = PCLPCFCostCodeManager::validateDelete(costCode, false); //Assert this.assertFalse(result); this.assertExpectedInfoLogMessage('System level cost headers cannot be deleted'); } }
Anatomy of a Unit Test
[SysTestTarget(classStr(PCLPCFCostCodeManager), UtilElementType::Class),
SysTestFixture(classStr(PCLPCFCostCodeTestSuite))]
Class attributes
• The [SysTestTarget] attribute must be present
• references the class / form / table the test is targeting
• If testing a form, formStr() method would be used, and UtilElementType::Form is supplied
• The [SysTestFixture] attribute denotes the text fixture used
• Generally test fixtures are shared among many like test cases
Anatomy of a Unit Test
class PCLPCFCostCodeManagerTests extends SysTestCase
Class definition
• Test case classes are named after the element they are testing
• Test class names are always post fixed with “Tests”, and extend the SysTestCase class
Anatomy of a Unit Test
[SysTestMethodAttribute,
SysTestGranularityAttribute(SysTestGranularity::Unit)]
Method attributes
• [SysTestMethodAttribute] tells the framework this method is a test
• Test case classes can have other “helper” methods
• [SysTestGranularityAttribute] is a grouping mechanism only
• Strictly speaking, is it optional, but in practice it should not be
Anatomy of a Unit Test
public void test_Component_Level_3_Cost_Code_Cannot_Be_Deleted()
Method signature
• The method signature should be a human readable string of text that concisely explains the intent of the test
• Spaces (if used) are represented by the underscore character
• Limited to 81 characters in length (X++ limitation)
• SysTest methods must follow these strict rules:
• Method signature MUST start with “test”
• Must return void
• Must be public
• Cannot have parameters
Anatomy of a Unit Test
//Arrange
PCLPCFProjCostHierarchyTreeNode costCode;
costCode.CostCode = PCLPCFCostCodeTestSuite::TestCostCodeNumber;
costCode.CostLevel = PCLPCFCostLevel::Component;
// skip insert validation logic, irrelevant to the test
costCode.doInsert();
Arrange block
• D365FO is extremely data driven, so this step generally involves inserting data into the database
• Initialize the absolute minimum data required to execute your test
• Use doInsert() and doUpdate() to avoid introducing table logic (keep it simple!)
• Some tests may not even need the record in the database!
Anatomy of a Unit Test
//Act
boolean result = PCLPCFCostCodeManager::validateDelete(costCode, false);
Act block
• Execute the focus of the test and record the result(s)
• Often this will be a single line of code
• If more, likely breaking one of the best practices (keep it simple, precise)
Anatomy of a Unit Test
//Assert
this.assertFalse(result);
this.assertExpectedInfoLogMessage('System level cost headers cannot be deleted');
Assert block
• Validate the result(s) of the Act block
• Check expected (or not expected) messages in the Infolog
• Remember that negative tests are just as important as positive tests
• The this keyword is used to access SysTest helper methods
• Devin Steinke’s Blog• http://coffeestain-it.blogspot.com/
• Dave Froslie – Microsoft Principal Software Architect• https://blogs.msdn.microsoft.com/dave_froslie/
• D365FO Wiki – Testing support in Visual Studio• https://docs.microsoft.com/en-us/dynamics365/unified-
operations/dev-itpro/perf-test/testing-validation
Additional Resources:
More questions?
Please contact Devin at: [email protected]