Geautomatiseerde tests van microsoft dynamics onder de knie krijgen

26 feb. 2024 | by Ralph Van Der Horst

Geautomatiseerde tests van Microsoft Dynamics onder de knie krijgen

Inleiding: Waarom Playwright voor Dynamics 365?

In mijn streven om dieper te duiken in de dynamiek van Microsoft Dynamics 365, wilde ik leren hoe je effectieve geautomatiseerde tests instelt voor deze complexe enterprise platform. Na uitgebreid onderzoek en praktische implementatie, ben ik overtuigd geraakt van de kracht van Playwright voor Dynamics 365 testautomatisering.

Microsoft promoot natuurlijk Playwright voor hun Dynamics 365 test automation - en dat begrijp ik volledig, aangezien dit ook een Microsoft product is. Maar na dieper onderzoek blijkt dat er solide technische redenen zijn waarom deze combinatie zo effectief is.

Microsoft’s Officiële Guidance

Bekijk gerust de officiële Microsoft Dynamics 365 Test Automation Setup pagina, maar ik heb deze aanzienlijk verbeterd in mijn eigen repository om relatieve paden en andere praktische verbeteringen te ondersteunen.

Waarom Playwright de Ideale Keuze is voor Microsoft Dynamics

Het gebruik van Playwright met Dynamics 365 voor testautomatisering biedt verschillende belangrijke voordelen die een weerspiegeling zijn van bredere trends in moderne webontwikkelings- en testpraktijken:

1. Cross-Browser Compatibiliteit op Enterprise Niveau

Multi-Browser Testing: Playwright ondersteunt native testing in Chromium, Firefox en WebKit, wat cruciaal is voor enterprise omgevingen waar gebruikers toegang hebben vanuit verschillende browsers en devices.

Consistent Behavior: Dynamics 365 applicaties moeten naadloos werken in diverse omgevingen - van corporate desktops tot mobile devices.

// Cross-browser test configuration
const browsers = ['chromium', 'firefox', 'webkit'];

for (const browserName of browsers) {
  test.describe(`Dynamics 365 - ${browserName}`, () => {
    test('Create Case functionality', async () => {
      const browser = await playwright[browserName].launch();
      // Test implementation
    });
  });
}

2. Geavanceerde UI Interactie Capabilities

Complex UI Handling: Dynamics 365 applicaties hebben complexe gebruikersinterfaces met nested frames, dynamic content, en modal dialogs.

Comprehensive API Set: Playwright biedt uitgebreide API’s voor het simuleren van gebruikersacties die echte gebruikersinteracties nauw nabootsen:

// Advanced UI interactions
await page.getByRole('button', { name: 'New Case' }).click();
await page.getByPlaceholder('Enter case title').fill('TEST CASE AUTOMATION');
await page.getByRole('combobox', { name: 'Customer' }).click();
await page.getByRole('option', { name: 'Learnautomatedtesting' }).click();

3. Superior Stability en Reliability

Automatic Waiting: Playwright’s automatische wachtfuncties elimineren de meeste timing issues die common zijn in enterprise applications.

Dynamic Content Handling: Efficiënte omgang met asynchrone operaties en lazy loading content.

Reduced Flakiness: Stabielere tests door intelligente element detection en state management.

4. Enterprise Authentication & State Management

Efficient Session Handling: De mogelijkheid om browser states (cookies, localStorage, sessionStorage) op te slaan en hergebruiken.

Authentication Optimization: Dramatische versnelling van test suites door session persistence.

Security Benefits: Minimale exposure van credentials door state reuse.

5. CI/CD Integration Excellence

Headless & Headed Modes:

  • Headless mode voor snelle CI/CD pipeline execution
  • Headed mode voor debugging en development

Pipeline Optimization: Perfect suited voor enterprise DevOps workflows.

// CI/CD optimized configuration
const config = {
  use: {
    headless: process.env.CI ? true : false,
    screenshot: 'only-on-failure',
    video: 'retain-on-failure'
  }
};

State Storage: De Game-Changer Feature

Wat ik echt geweldig vind, is de state storage functionaliteit in Playwright waarmee je browserstorage kunt vastleggen en hergebruiken. Dit omvat cookies, lokale opslag en sessieopslag tijdens test runs. Deze functie is bijzonder handig om login screens te omzeilen en snel tests in te stellen door sessiestatus te behouden.

Implementatie van Global Setup

import { chromium } from '@playwright/test';
import fs from 'fs';
import { login } from './helpers/auth-helper';

async function globalSetup(config) {
  // Ensure the storage state directory exists
  const storageStateDirectory = './auth-states';
  if (!fs.existsSync(storageStateDirectory)) {
    fs.mkdirSync(storageStateDirectory, { recursive: true });
  }

  const browser = await chromium.launch({ headless: false });
  const page = await browser.newPage();
  
  console.log(`Authenticating user: ${username}`);
  await login(page, orgurl, username, password);

  // Save the storage state to the relative path
  await page.context().storageState({
    path: './auth-states/authenticated-state.json',
  });

  await browser.close();
}

export default globalSetup;

Advanced State Management

// Different states for different user roles
const states = {
  admin: './auth-states/admin-state.json',
  serviceAgent: './auth-states/service-agent-state.json',
  customer: './auth-states/customer-state.json'
};

// Use specific state per test
test.use({ storageState: states.serviceAgent });

Praktische Toepassingen van State Storage

1. Session Persistence Across Tests

Authentication Efficiency: De meest voorkomende toepassing is gebruikersauthenticatie. Inloggen kan tijdrovend en repetitief zijn voor elke test case.

Speed Optimization: Door na het inloggen de storage state op te slaan, kun je het aanmeldingsproces bij volgende tests omzeilen, waardoor de test suite dramatisch wordt versneld.

// Before: Each test logs in separately (slow)
test('Test 1', async ({ page }) => {
  await page.goto('/login');
  await page.fill('[name="username"]', username);
  await page.fill('[name="password"]', password);
  await page.click('[type="submit"]');
  // Actual test logic...
});

// After: Use stored authentication state (fast)
test.use({ storageState: 'auth-state.json' });
test('Test 1', async ({ page }) => {
  await page.goto('/dashboard'); // Already authenticated!
  // Actual test logic...
});

2. Environment Consistency

Standardized Configuration: Storage state zorgt ervoor dat elke test begint met dezelfde vooraf gedefinieerde omgevingsconfiguratie.

Reduced Flakiness: Uitgaan van een bekende state helpt bij het verminderen van test flakiness en zorgt voor consistentie.

3. Testing Efficiency

Speed Improvements:

  • 70-90% sneller dan herhaalde UI-based authentication
  • Minder server load door reduced repetitive interactions
  • Optimale resource utilization in CI/CD pipelines

Resource Optimization: Verminderde belasting van test infrastructure door efficiënte state reuse.

4. Real User Experience Simulation

Stateful Interactions: Moderne web applicaties zijn afhankelijk van client-side storage voor UX management.

Accurate Testing: Door specifieke storage states vast te leggen, kunnen tests realistische gebruikersscenario’s simuleren.

// Simulate user with shopping cart
const userWithCartState = {
  localStorage: {
    'shopping-cart': JSON.stringify([
      { id: 1, product: 'Service License', quantity: 2 }
    ])
  },
  cookies: [
    { name: 'session-id', value: 'abc123', domain: 'dynamics.com' }
  ]
};

5. Security & Compliance Benefits

Credential Protection: State storage abstraheert gevoelige informatie van test scripts.

Secure Practices:

  • Authentication state kan één keer worden gegenereerd
  • Veilig opgeslagen en hergebruikt
  • Minimale exposure van login credentials

6. Flexible Test Design

Multiple States voor Different Scenarios: Je kunt verschillende storage states creëren voor diverse test scenarios:

const testStates = {
  newUser: './states/new-user-state.json',
  premiumUser: './states/premium-user-state.json',
  adminUser: './states/admin-user-state.json',
  userWithData: './states/user-with-populated-data.json'
};

// Use appropriate state per test type
test.describe('Premium Features', () => {
  test.use({ storageState: testStates.premiumUser });
  
  test('Access premium dashboard', async ({ page }) => {
    // Test runs with premium user privileges
  });
});

Vergelijking met Andere Testing Frameworks

State Storage in Selenium/WebDriverIO

Voor Selenium-based applicaties zoals WebDriverIO en Katalon is state storage ook mogelijk, maar:

Niet out-of-the-box beschikbaar ❌ Complexere implementatie vereist ❌ Meer boilerplate code nodig ❌ Minder reliable cross-browser support

// WebDriverIO - Manual state management (complex)
const cookieState = await browser.getCookies();
await fs.writeFile('./cookies.json', JSON.stringify(cookieState));

// Later restoration requires manual handling
const savedCookies = JSON.parse(await fs.readFile('./cookies.json'));
for (const cookie of savedCookies) {
  await browser.setCookies(cookie);
}

State Storage in Cypress

Cypress heeft vergelijkbare uitdagingen:

Custom implementation vereist voor state persistence ❌ Limited cross-browser support (vooral Chromium-based) ❌ More complex session management

// Cypress - Manual session handling
cy.session('user-session', () => {
  cy.visit('/login');
  cy.get('[name="username"]').type(username);
  cy.get('[name="password"]').type(password);
  cy.get('[type="submit"]').click();
}, {
  validate() {
    cy.request('/api/user').its('status').should('eq', 200);
  }
});

Waarom Playwright Superieur is

Native state storage support ✅ Cross-browser consistencyEnterprise-grade reliabilityMinimal configuration required ✅ Microsoft integration optimization

Persoonlijke Note: Hoewel ik denk dat Playwright zijn plaats heeft verdiend in het testing ecosysteem en een fantastisch product is, blijft mijn persoonlijke voorkeur bij het WebDriverIO project. Vooral met de toekomstige BiDi support en andere advanced features. Maar objectiviteit is belangrijk - Playwright’s state management is onbetwistbaar superieur.

Praktische Implementatie: Dynamics 365 Case Creation

Het Playwright script automatiseert het volledige proces van het aanmaken van een nieuwe case in de Customer Service Hub applicatie van Microsoft Dynamics 365.

Script Breakdown

1. Environment Setup & Configuration

import { test, expect } from '@playwright/test';
import dotenv from 'dotenv';
import { commonselectors } from '../selectors/commonselectors.json';
import { NavigateToApps, StringFormat } from '../helpers/common-helper';

// Load environment variables (never commit .env to version control!)
dotenv.config();

const {
  CUSTOMER_SERVICE_HUB_APP_ID,
  ORG_URL,
  USERNAME,
  PASSWORD
} = process.env;

2. Test Definition & Execution

test('Create Case in Customer Service Hub', async ({ page }) => {
  // Navigate to Customer Service Hub application
  const appId = process.env.CUSTOMER_SERVICE_HUB_APP_ID;
  await NavigateToApps(page, appId);
  
  // Open Cases section
  const casesSelector = StringFormat(
    commonselectors.menuItem, 
    'Cases'
  );
  await page.click(casesSelector);
  
  // Create new case
  await page.getByRole('button', { name: 'New' }).click();
  
  // Fill case details
  const caseTitle = `TEST CASE AUTOMATION - ${Math.floor(Math.random() * 10000)}`;
  await page.getByPlaceholder('Enter case title').fill(caseTitle);
  
  // Select customer
  await page.getByRole('button', { name: 'Customer' }).click();
  await page.getByPlaceholder('Search for Customer').fill('Learnautomatedtesting');
  await page.getByRole('treeitem', { name: 'Learnautomatedtesting' }).click();
  
  // Add case description
  await page.getByLabel('Description').fill(
    'Automated test case: Coffee machine power issue - unable to turn on device. Please investigate hardware connection and power supply.'
  );
  
  // Save the case
  await page.getByRole('button', { name: 'Save' }).click();
  
  // Wait for save completion
  await page.waitForLoadState('networkidle');
  
  // Verify case creation
  const caseNumber = await page.getByLabel('Case Number').inputValue();
  console.log(`Successfully created case: ${caseNumber}`);
  
  // Assertions
  expect(caseNumber).toBeTruthy();
  expect(caseNumber).toMatch(/^\d+$/); // Should be numeric
});

Advanced Features Implementation

3. Error Handling & Resilience

test('Create Case with Error Handling', async ({ page }) => {
  try {
    // Wrap in try-catch for robust error handling
    await test.step('Navigate to Customer Service Hub', async () => {
      const appId = process.env.CUSTOMER_SERVICE_HUB_APP_ID;
      await NavigateToApps(page, appId);
      await expect(page).toHaveTitle(/Customer Service Hub/);
    });

    await test.step('Create new case', async () => {
      await page.getByRole('button', { name: 'New' }).click();
      await expect(page.getByText('New Case')).toBeVisible();
    });

    await test.step('Fill mandatory fields', async () => {
      const caseTitle = `AUTOMATED TEST - ${new Date().toISOString()}`;
      await page.getByPlaceholder('Enter case title').fill(caseTitle);
      
      // Verify field was filled
      await expect(page.getByPlaceholder('Enter case title')).toHaveValue(caseTitle);
    });

  } catch (error) {
    // Capture screenshot on failure
    await page.screenshot({ 
      path: `./test-results/failure-${Date.now()}.png`,
      fullPage: true 
    });
    throw error;
  }
});

4. Data-Driven Testing

const testCases = [
  {
    title: 'Hardware Issue',
    customer: 'Learnautomatedtesting',
    description: 'Coffee machine power failure',
    priority: 'High'
  },
  {
    title: 'Software Bug',
    customer: 'TestCustomer Corp',
    description: 'Application crash on startup',
    priority: 'Medium'
  }
];

for (const testCase of testCases) {
  test(`Create Case: ${testCase.title}`, async ({ page }) => {
    // Dynamic test execution based on test data
    await createCase(page, testCase);
  });
}

Best Practices voor Dynamics 365 Testing

1. Environment Management

// .env.example (commit this to repo)
ORG_URL=https://your-org.crm.dynamics.com
CUSTOMER_SERVICE_HUB_APP_ID=your-app-id
USERNAME=test-user@domain.com
# PASSWORD - set in actual .env file (never commit)

// Environment-specific configurations
const configs = {
  development: {
    headless: false,
    slowMo: 1000,
    timeout: 30000
  },
  staging: {
    headless: true,
    timeout: 15000
  },
  production: {
    headless: true,
    timeout: 10000,
    retries: 2
  }
};

2. Robust Selectors

// Use semantic selectors over brittle XPath
// ✅ Good - Role-based selectors
await page.getByRole('button', { name: 'Save' }).click();
await page.getByLabel('Case Title').fill('Test Case');

// ❌ Avoid - Brittle XPath selectors  
await page.click('//div[@class="specific-class-that-might-change"]');

// ✅ Good - Test ID selectors (when available)
await page.getByTestId('case-save-button').click();

3. Wait Strategies

// Comprehensive wait strategies for Dynamics 365
await page.waitForLoadState('networkidle'); // Wait for network to be idle
await page.waitForSelector('[data-id="case-form"]', { state: 'visible' });
await page.waitForFunction(() => window.Xrm?.Page?.data?.entity?.save);

// Custom wait for Dynamics-specific operations
async function waitForDynamicsReady(page) {
  await page.waitForFunction(() => {
    return window.Xrm && 
           window.Xrm.Page && 
           window.Xrm.Page.data && 
           window.Xrm.Page.data.entity;
  });
}

4. Test Organization

// Organized test structure
test.describe('Customer Service Hub', () => {
  test.describe('Case Management', () => {
    test.beforeEach(async ({ page }) => {
      // Common setup for case tests
      await NavigateToApps(page, CUSTOMER_SERVICE_HUB_APP_ID);
    });

    test('Create case', async ({ page }) => { /* test */ });
    test('Update case', async ({ page }) => { /* test */ });
    test('Delete case', async ({ page }) => { /* test */ });
  });

  test.describe('Knowledge Base', () => {
    // Knowledge base specific tests
  });
});

Performance Optimization Strategies

1. Parallel Execution

// playwright.config.js
export default {
  workers: process.env.CI ? 2 : 4, // Adjust based on environment
  fullyParallel: true,
  projects: [
    {
      name: 'Customer Service Hub',
      testMatch: '**/customer-service/**/*.spec.js',
      use: { ...devices['Desktop Chrome'] }
    }
  ]
};

2. Smart Test Isolation

// Use storage state for fast test isolation
test.describe('Authenticated Tests', () => {
  test.use({ storageState: './auth-states/service-agent.json' });
  
  test('Quick case creation', async ({ page }) => {
    // Test runs with pre-authenticated state
    await page.goto('/main.aspx?appid=' + CUSTOMER_SERVICE_HUB_APP_ID);
    // Immediately ready for testing - no login required!
  });
});

Troubleshooting Common Issues

1. Authentication Problems

// Debug authentication issues
test('Debug Auth State', async ({ page }) => {
  // Check if auth state is valid
  await page.goto('/main.aspx');
  
  const isLoggedIn = await page.locator('.user-menu').isVisible();
  if (!isLoggedIn) {
    console.error('Authentication state invalid - regenerating...');
    // Trigger re-authentication
  }
});

2. Dynamics-Specific Timing Issues

// Handle Dynamics loading states
async function waitForDynamicsForm(page) {
  // Wait for Dynamics form to be fully loaded
  await page.waitForFunction(() => {
    const form = window.Xrm?.Page?.ui?.formSelector?.getCurrentItem();
    return form && form.getLabel();
  });
}

Repository en Learning Resources

De complete implementatie is beschikbaar in mijn GitHub repository: https://github.com/learn-automated-testing/dynamics_playwright/

Wat je vindt in de Repository:

Complete setup configuratie ✅ Werkende voorbeelden voor Dynamics 365 testing ✅ State management implementatie
Best practices documentation ✅ Troubleshooting guidesPerformance optimization tips

Komende Training

Een uitgebreide training over Dynamics 365 test automatisering met Playwright zal ik de komende maanden op mijn website publiceren. Deze zal include:

  • Hands-on workshops
  • Video tutorials
  • Advanced scenario’s
  • Enterprise patterns
  • CI/CD integration

Conclusie

Playwright’s combinatie met Microsoft Dynamics 365 biedt een krachtige, efficiënte en betrouwbare test automation solution. De state storage functionaliteit alleen al rechtvaardigt de keuze voor Playwright, met dramatische verbeteringen in test execution speed en reliability.

Key Takeaways

Native state management elimineert repetitive authentication ✅ Cross-browser testing ensures enterprise compatibility ✅ Robust selectors en smart waiting improve test stability
Microsoft integration provides optimal Dynamics 365 support ✅ Enterprise-grade features support complex business scenarios

Of je nu een individual tester bent of deel uitmaakt van een enterprise development team, Playwright’s capabilities voor Dynamics 365 testing zijn game-changing. De investering in het leren van deze toolchain betaalt zich snel terug door de dramatische efficiency gains en quality improvements.

Start vandaag met het experimenteren met de repository code en ervaar zelf hoe Dynamics 365 testing kan transformeren van een time-consuming challenge naar een streamlined, reliable process.


Voor de nieuwste updates, advanced tutorials en community support, volg de ontwikkelingen op de GitHub repository en mijn aankomende training materialen.

by Ralph Van Der Horst

arrow right
back to blog

share this article

Relevant articles

Gratis Cursus Testautomatisering met Katalon Studio en Salesforce

Gratis Cursus Testautomatisering met Katalon Studio en Salesforce

Stel de oauth2-client in voor Salesforce dev env voor API-testen met curl postman en Katalon

Stel de oauth2-client in voor Salesforce dev env voor API-testen met curl postman en Katalon

Hoe een Allure-rapport te serveren op GitHub-pagina's Een stap voor stap handleiding

Hoe een Allure-rapport te serveren op GitHub-pagina's Een stap voor stap handleiding