automated testing in dynamics 365 for operations

31
Automated Testing in Dynamics 365 For Operations Devin Steinke – Solution Architect, PCL

Upload: others

Post on 25-Feb-2022

7 views

Category:

Documents


0 download

TRANSCRIPT

Automated Testing in Dynamics 365 For Operations

Devin Steinke – Solution Architect, PCL

Testing Principals

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

$

¢

Test Pyramid

Unit

Component

Integration

Business Cycle

Or System

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

Continuous IntegrationDemo

Test analytics

Test run summary

Data Rollback

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

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

SysTestDemo

SysTest class

Task RecorderDemo

Task recorder

Import task recording into Visual Studio

SysTest From Task Recording

• 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]

THANK YOU FOR ATTENDING!