Introduction: The Hidden Gap in CRM Test Automation
Many teams have automated the actions that interact with Dynamics 365 CRM — creating records, triggering flows, updating data — but still rely on humans to validate the results. Someone logs into CRM, searches for the record, checks the fields, and confirms that everything looks right.
This manual step is slow, inconsistent, and undermines the value of automation.
In this post, we’ll close that gap by validating CRM data programmatically using the Dataverse Web API.
The Current State: Automated Actions, Manual Validation
A typical pattern looks like this:
- An automated test triggers a Power Automate flow.
- The flow creates a Contact or other Dataverse record.
- The test receives a 200 OK response.
- A human logs into CRM to confirm the data is correct.
The automation stops halfway. The system confirms the flow ran, but not that the data is correct.
The Goal: Closing the Loop with Automated CRM Validation
The next logical step is to automate the validation itself. We want to confirm:
- The record exists.
- The fields contain the expected values.
- Related records were created.
- Business rules, plugins, or flows executed correctly.
All without logging into the CRM UI.
Solution Overview: Validating Dynamics 365 Data via the Dataverse Web API
The Dataverse Web API gives you direct, secure access to CRM data. Using it, your automated tests can:
- Query tables (entities)
- Filter by field values
- Expand related records
- Assert business logic outcomes
This turns your test suite into a true end‑to‑end validation pipeline.
Architecture: How the End‑to‑End Test Flow Works
A simple architecture looks like this:
Automated Test ↓API Call to Power Automate Flow ↓Flow Creates CRM Data ↓Automated Test Calls Dataverse Web API ↓Validation of Data, Relationships, and Business Logic
This closes the loop: the test both creates and verifies the data.
Why Validate Through the API Instead of the UI
UI automation is slow, brittle, and environment‑dependent. API validation is:
- Fast — milliseconds instead of minutes
- Reliable — no DOM changes or UI latency
- CI/CD‑friendly — runs headless
- Precise — query exactly what you need
- Secure — uses service principals, not user accounts
It’s the same API used internally by Power Apps and Power Automate.
Authentication: Giving Your Test Secure Access to Dataverse
To query Dataverse, your test needs an access token. The recommended approach is:
1. Create an Azure AD App Registration
This becomes your service principal.
2. Create an Application User in Dataverse
Assign it a minimal security role — often read‑only for validation.
3. Authenticate Using Client Credentials
Your test requests a token like this:
POST https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/tokenclient_id=xxxxclient_secret=xxxxscope=https://{org}.crm.dynamics.com/.defaultgrant_type=client_credentials
This gives you a bearer token for API calls.
Querying Dataverse: Verifying That the Record Exists
Once authenticated, your test can query Dataverse directly.
Example: validate that a Contact was created:
GET https://{org}.crm.dynamics.com/api/data/v9.2/contacts?$filter=emailaddress1 eq 'test@example.com'
Your test should assert:
- Status code 200
- Exactly one record returned
- Field values match expectations
This alone replaces the manual “log in and search” step.
Validating Field Values and Business Rules
Beyond existence, you can validate:
- Default values set by plugins
- Calculated fields
- Rollup fields
- Flow‑generated updates
- Status or state transitions
Your test becomes a safety net for business logic, not just data creation.
Checking Related Records with OData Expansions
Dataverse supports $expand, allowing you to validate relationships in a single call.
Example: confirm the Contact is linked to the correct Account:
GET https://{org}.crm.dynamics.com/api/data/v9.2/contacts?$filter=emailaddress1 eq 'test@example.com'&$expand=parentcustomerid_account($select=name)
You can validate:
- The Account exists
- The relationship is correct
- The Account has expected attributes
This is especially useful for scenarios where flows or plugins create related records.
Handling Asynchronous Operations and Retry Logic
Some CRM operations are asynchronous — plugins, flows, or rollups may take a moment to complete.
Your test should include:
- Retry logic
- Exponential backoff
- A maximum timeout
This ensures your validation waits for the system to settle before asserting.
Full Example: Automated Test Flow from Trigger to Validation
A complete test might:
- Trigger a Power Automate flow via HTTP.
- Receive a correlation ID or expected email address.
- Wait briefly or use retry logic.
- Query Dataverse for the record.
- Validate:
- Field values
- Status codes
- Related records
- Business logic outcomes
- Optionally delete the test data.
This gives you a fully automated, repeatable test.
Code Snippet for API‑Based Validation (e.g., Python, C#, Postman)
Here’s a simple Python example:
import requeststoken = get_token() # your OAuth functionheaders = { "Authorization": f"Bearer {token}", "Accept": "application/json"}url = f"https://{org}.crm.dynamics.com/api/data/v9.2/contacts"params = { "$filter": "emailaddress1 eq 'test@example.com'", "$select": "firstname,lastname,emailaddress1"}response = requests.get(url, headers=headers, params=params)data = response.json()assert data['value'][0]['firstname'] == 'Jamie'
Swap in your own fields and entity names.
Cleaning Up Test Data (Optional but Recommended)
If your tests create data, consider cleaning it up:
- Delete test records
- Use a naming convention (e.g., “Test_”)
- Run cleanup jobs in CI/CD
This keeps your CRM environment tidy and predictable.
Common Pitfalls and How to Avoid Them
- Using user accounts instead of service principals
Leads to MFA issues and brittle tests. - Not waiting for async operations
Causes intermittent failures. - Querying too broadly
Always filter by unique identifiers. - Hard‑coding environment URLs
Use configuration variables.
Conclusion: Bringing True End‑to‑End Confidence to CRM Automation
By validating CRM data through the Dataverse Web API, you eliminate the last manual step in your automation pipeline. Your tests become faster, more reliable, and more meaningful — giving you true end‑to‑end confidence that your flows, plugins, and business logic are working exactly as intended.
Leave a comment