๐Ÿงช Day 12: Automated Testing - Hands-On Lab Guide

Practical exercises for unit testing in CI/CD pipelines (Windows Edition)

Duration: 4 hours | Module: Testing & Artifacts

๐Ÿ’ป Windows Environment: All commands are optimized for Windows PowerShell and Command Prompt.
โš ๏ธ Prerequisites Check:
  1. Java JDK 11+ installed (for Maven labs)
  2. .NET SDK 6.0+ installed (for .NET labs)
  3. Maven installed and in PATH
  4. Azure DevOps project from Day 11
  5. Code editor (VS Code or IntelliJ)

Lab 1: Setup Java Project with Tests ๐Ÿ”จ

Objective: Create a Maven project with JUnit tests
Time: 25 minutes

Exercise 1.1: Create Maven Project with Tests

1 Create Project Directory
# Open PowerShell cd $env:USERPROFILE\Desktop mkdir testing-demo cd testing-demo
2 Create Maven Project
# Create Maven project with JUnit mvn archetype:generate ` -DgroupId=com.example ` -DartifactId=calculator ` -DarchetypeArtifactId=maven-archetype-quickstart ` -DarchetypeVersion=1.4 ` -DinteractiveMode=false cd calculator
3 Update pom.xml for JUnit 5
# Open pom.xml notepad pom.xml

Replace the entire content with:

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>calculator</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <junit.version>5.9.3</junit.version> </properties> <dependencies> <!-- JUnit 5 --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0</version> </plugin> </plugins> </build> </project>

Save and close (Ctrl+S, Alt+F4)

4 Create Calculator Class
# Create source file notepad src\main\java\com\example\Calculator.java

Add this code:

package com.example; public class Calculator { public int add(int a, int b) { return a + b; } public int subtract(int a, int b) { return a - b; } public int multiply(int a, int b) { return a * b; } public int divide(int a, int b) { if (b == 0) { throw new IllegalArgumentException("Cannot divide by zero"); } return a / b; } }

Save and close

5 Verify Project Structure
tree /F /A calculator +---src +---main | +---java | +---com | +---example | Calculator.java +---test +---java +---com +---example (tests will go here)
โœ… Checkpoint: Maven project created with Calculator class

Lab 2: Write Unit Tests with JUnit 5 โœ๏ธ

Objective: Create comprehensive unit tests for Calculator
Time: 30 minutes

Exercise 2.1: Create Test Class

1 Create Test File
# Create test directory if needed mkdir src\test\java\com\example -Force # Create test file notepad src\test\java\com\example\CalculatorTest.java
2 Write Comprehensive Tests

Add complete test class:

package com.example; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import static org.junit.jupiter.api.Assertions.*; public class CalculatorTest { private Calculator calculator; @BeforeEach void setUp() { calculator = new Calculator(); } // Addition Tests @Test @DisplayName("Test addition of two positive numbers") void testAdd_PositiveNumbers() { // Arrange int a = 5; int b = 3; // Act int result = calculator.add(a, b); // Assert assertEquals(8, result, "5 + 3 should equal 8"); } @Test @DisplayName("Test addition with negative numbers") void testAdd_NegativeNumbers() { assertEquals(-2, calculator.add(-5, 3)); assertEquals(-8, calculator.add(-5, -3)); } @Test @DisplayName("Test addition with zero") void testAdd_WithZero() { assertEquals(5, calculator.add(5, 0)); assertEquals(5, calculator.add(0, 5)); } // Subtraction Tests @Test @DisplayName("Test subtraction of two numbers") void testSubtract() { assertEquals(2, calculator.subtract(5, 3)); assertEquals(8, calculator.subtract(5, -3)); } // Multiplication Tests @Test @DisplayName("Test multiplication") void testMultiply() { assertEquals(15, calculator.multiply(5, 3)); assertEquals(-15, calculator.multiply(5, -3)); assertEquals(0, calculator.multiply(5, 0)); } // Division Tests @Test @DisplayName("Test division of two numbers") void testDivide() { assertEquals(2, calculator.divide(6, 3)); assertEquals(-2, calculator.divide(6, -3)); } @Test @DisplayName("Test division by zero throws exception") void testDivide_ByZero_ThrowsException() { // Assert that exception is thrown Exception exception = assertThrows( IllegalArgumentException.class, () -> calculator.divide(5, 0) ); // Assert exception message assertEquals("Cannot divide by zero", exception.getMessage()); } }

Save and close

3 Understanding the Tests
Test Annotations:
  • @Test - Marks method as a test
  • @BeforeEach - Runs before each test
  • @DisplayName - Descriptive test name
Assertions:
  • assertEquals(expected, actual) - Check equality
  • assertThrows(exception, code) - Check exception thrown
  • assertTrue(condition) - Check boolean
โœ… Checkpoint: 8 unit tests written covering all methods

Lab 3: Run Tests Locally โ–ถ๏ธ

Objective: Execute tests and view results
Time: 20 minutes

Exercise 3.1: Run Tests with Maven

1 Run All Tests
# Navigate to project directory cd $env:USERPROFILE\Desktop\testing-demo\calculator # Run tests mvn test [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.example.CalculatorTest [INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.123 s [INFO] [INFO] Results: [INFO] [INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] BUILD SUCCESS
2 Run Specific Test
# Run single test class mvn test -Dtest=CalculatorTest # Run single test method mvn test -Dtest=CalculatorTest#testAdd_PositiveNumbers
3 View Test Reports
# Open test report in browser start target\surefire-reports\com.example.CalculatorTest.txt # Or view HTML report start target\surefire-reports\index.html
๐Ÿ’ก Test Report Location:
XML: target\surefire-reports\TEST-*.xml
TXT: target\surefire-reports\*.txt
4 Clean and Test
# Clean previous results and run tests mvn clean test

Exercise 3.2: Introduce a Failing Test

1 Add Failing Test
notepad src\test\java\com\example\CalculatorTest.java

Add this test at the end of the class:

@Test @DisplayName("Intentional failing test") void testIntentionalFailure() { // This will fail assertEquals(10, calculator.add(5, 3), "Intentionally wrong"); }

Save and close

2 Run Tests Again
mvn test [ERROR] Failures: [ERROR] CalculatorTest.testIntentionalFailure:XX Intentionally wrong expected: <10> but was: <8> [INFO] [INFO] Results: [INFO] [ERROR] Failures: 1 [INFO] Tests run: 9, Failures: 1, Errors: 0, Skipped: 0 [INFO] [INFO] BUILD FAILURE
3 Fix the Test

Change the assertion to correct value:

assertEquals(8, calculator.add(5, 3), "Fixed assertion");

Or remove the test entirely, then run mvn test again.

โœ… Checkpoint: Can run tests locally and see pass/fail results

Lab 4: .NET Unit Tests with xUnit ๐Ÿ”ท

Objective: Create .NET project with xUnit tests
Time: 35 minutes

Exercise 4.1: Create .NET Solution with Tests

1 Create Solution and Projects
# Navigate to testing directory cd $env:USERPROFILE\Desktop\testing-demo # Create solution dotnet new sln -n CalculatorApp # Create class library project dotnet new classlib -n Calculator.Core # Create xUnit test project dotnet new xunit -n Calculator.Tests # Add projects to solution dotnet sln add Calculator.Core\Calculator.Core.csproj dotnet sln add Calculator.Tests\Calculator.Tests.csproj # Add reference from test project to core project dotnet add Calculator.Tests\Calculator.Tests.csproj reference Calculator.Core\Calculator.Core.csproj
2 Create Calculator Class
# Delete default class Remove-Item Calculator.Core\Class1.cs # Create Calculator class notepad Calculator.Core\Calculator.cs

Add this code:

namespace Calculator.Core; public class Calculator { public int Add(int a, int b) { return a + b; } public int Subtract(int a, int b) { return a - b; } public int Multiply(int a, int b) { return a * b; } public int Divide(int a, int b) { if (b == 0) { throw new DivideByZeroException("Cannot divide by zero"); } return a / b; } }

Save and close

3 Write xUnit Tests
# Delete default test Remove-Item Calculator.Tests\UnitTest1.cs # Create test file notepad Calculator.Tests\CalculatorTests.cs

Add comprehensive tests:

using Calculator.Core; using Xunit; namespace Calculator.Tests; public class CalculatorTests { private readonly Core.Calculator _calculator; public CalculatorTests() { _calculator = new Core.Calculator(); } // Addition Tests [Fact] public void Add_TwoPositiveNumbers_ReturnsSum() { // Arrange int a = 5; int b = 3; // Act int result = _calculator.Add(a, b); // Assert Assert.Equal(8, result); } [Theory] [InlineData(5, 3, 8)] [InlineData(-5, 3, -2)] [InlineData(-5, -3, -8)] [InlineData(0, 5, 5)] public void Add_VariousInputs_ReturnsCorrectSum(int a, int b, int expected) { var result = _calculator.Add(a, b); Assert.Equal(expected, result); } // Subtraction Tests [Fact] public void Subtract_TwoNumbers_ReturnsDifference() { Assert.Equal(2, _calculator.Subtract(5, 3)); Assert.Equal(8, _calculator.Subtract(5, -3)); } // Multiplication Tests [Theory] [InlineData(5, 3, 15)] [InlineData(5, -3, -15)] [InlineData(5, 0, 0)] public void Multiply_VariousInputs_ReturnsProduct(int a, int b, int expected) { Assert.Equal(expected, _calculator.Multiply(a, b)); } // Division Tests [Fact] public void Divide_TwoNumbers_ReturnsQuotient() { Assert.Equal(2, _calculator.Divide(6, 3)); Assert.Equal(-2, _calculator.Divide(6, -3)); } [Fact] public void Divide_ByZero_ThrowsException() { // Assert var exception = Assert.Throws<DivideByZeroException>( () => _calculator.Divide(5, 0) ); Assert.Equal("Cannot divide by zero", exception.Message); } }

Save and close

4 Run .NET Tests
# Build solution dotnet build # Run all tests dotnet test Starting test execution, please wait... A total of 1 test files matched the specified pattern. Passed! - Failed: 0, Passed: 10, Skipped: 0, Total: 10, Duration: 45 ms
5 Run Tests with Detailed Output
# Verbose output dotnet test --logger "console;verbosity=detailed" # Generate test results file dotnet test --logger "trx;LogFileName=testresults.trx"
โœ… Checkpoint: .NET project with 10 xUnit tests running successfully

Lab 5: Add Tests to Azure Pipeline ๐Ÿ”—

Objective: Integrate tests into CI/CD pipeline
Time: 40 minutes

Exercise 5.1: Create Pipeline for Maven Tests

1 Initialize Git Repository
# Navigate to Maven project cd $env:USERPROFILE\Desktop\testing-demo\calculator # Initialize Git git init git add . git commit -m "Initial commit with tests"
2 Create Pipeline YAML
notepad azure-pipelines.yml

Add complete pipeline:

# Maven Test Pipeline trigger: - main pool: vmImage: 'windows-latest' steps: # Run tests - task: Maven@3 displayName: 'Run Unit Tests' inputs: mavenPomFile: 'pom.xml' goals: 'clean test' publishJUnitResults: true testResultsFiles: '**/surefire-reports/TEST-*.xml' testRunTitle: 'Calculator Unit Tests' javaHomeOption: 'JDKVersion' jdkVersionOption: '1.11' # Package application (if tests pass) - task: Maven@3 displayName: 'Package Application' inputs: mavenPomFile: 'pom.xml' goals: 'package' condition: succeeded()

Save and close

3 Push to Azure Repos
# Add and commit pipeline git add azure-pipelines.yml git commit -m "Add test pipeline" # Push to Azure Repos (get URL from Azure DevOps) git remote add origin YOUR_AZURE_REPOS_URL git push -u origin main
4 Create Pipeline in Azure DevOps
  • Go to Azure DevOps โ†’ Pipelines
  • Click "New Pipeline"
  • Select "Azure Repos Git"
  • Choose your repository
  • Select "Existing Azure Pipelines YAML file"
  • Path: /azure-pipelines.yml
  • Click "Run"
5 Watch Pipeline Execute

You'll see:

  1. โœ… Job starts
  2. โœ… Maven runs tests
  3. โœ… Test results published
  4. โœ… Package step runs (if tests pass)
  5. โœ… Build succeeds

Exercise 5.2: Create Pipeline for .NET Tests

1 Create .NET Pipeline YAML
# Navigate to .NET solution cd $env:USERPROFILE\Desktop\testing-demo\CalculatorApp # Initialize Git git init notepad azure-pipelines.yml

Add .NET test pipeline:

# .NET Test Pipeline trigger: - main pool: vmImage: 'windows-latest' steps: # Restore packages - task: DotNetCoreCLI@2 displayName: 'Restore NuGet Packages' inputs: command: 'restore' # Build solution - task: DotNetCoreCLI@2 displayName: 'Build Solution' inputs: command: 'build' arguments: '--configuration Release' # Run tests - task: DotNetCoreCLI@2 displayName: 'Run Unit Tests' inputs: command: 'test' projects: '**/*Tests.csproj' arguments: '--no-build --configuration Release' publishTestResults: true testRunTitle: 'Calculator xUnit Tests'

Save, commit, and push to Azure Repos

โœ… Checkpoint: Tests running automatically in pipeline

Lab 6: Viewing Test Results in Azure DevOps ๐Ÿ“Š

Objective: Navigate and understand test results
Time: 25 minutes

Exercise 6.1: Explore Test Results Tab

1 Access Test Results
  • Go to your pipeline run
  • Click "Tests" tab at the top
  • You'll see test summary
2 Test Summary View

You'll see:

  • Total Tests: 8 (or 10 for .NET)
  • Passed: 8 โœ…
  • Failed: 0
  • Pass Rate: 100%
  • Duration: ~3 seconds
3 Individual Test Details

Click on any test to see:

  • Test name and display name
  • Pass/Fail status
  • Execution time
  • Error messages (if failed)
  • Stack trace (if failed)
4 Filter and Search

Use filters to:

  • Show only failed tests
  • Search by test name
  • Sort by duration
  • Group by outcome
5 Test Trends
  • Click "Analytics" or "Trends"
  • View test pass rate over time
  • See which tests fail most often
  • Identify flaky tests
โœ… Checkpoint: Understand how to navigate test results

Lab 7: Failing Tests Block Deployment ๐Ÿšซ

Objective: See how failed tests prevent deployment
Time: 30 minutes

Exercise 7.1: Introduce Failing Test

1 Add Failing Test to Java Project
cd $env:USERPROFILE\Desktop\testing-demo\calculator notepad src\test\java\com\example\CalculatorTest.java

Add this failing test:

@Test @DisplayName("This test will fail") void testBrokenLogic() { // Intentionally wrong assertion assertEquals(100, calculator.add(5, 3), "Expected 100 but calculator returns 8"); }
2 Commit and Push
git add . git commit -m "Add failing test intentionally" git push
3 Watch Pipeline Fail
  • Pipeline triggers automatically
  • Build step succeeds โœ…
  • Test step FAILS โŒ
  • Pipeline stops
  • Deployment blocked ๐Ÿšซ
4 View Failure Details
  • Go to "Tests" tab
  • See: 8 Passed, 1 Failed
  • Click on failed test
  • Read error message:
    Expected: 100 Actual: 8 Message: Expected 100 but calculator returns 8
5 Fix the Test

Change the assertion to correct value:

assertEquals(8, calculator.add(5, 3));

Commit and push:

git add . git commit -m "Fix failing test" git push
6 Watch Pipeline Succeed
  • New pipeline run triggers
  • All tests pass โœ…
  • Build succeeds โœ…
  • Ready for deployment ๐Ÿš€
โœ… Checkpoint: Understand how tests act as quality gate

๐ŸŽฏ Lab Summary

What You've Accomplished:

  • โœ… Created Java project with JUnit 5 tests
  • โœ… Created .NET project with xUnit tests
  • โœ… Written comprehensive unit tests (AAA pattern)
  • โœ… Ran tests locally and viewed results
  • โœ… Added tests to Azure Pipelines
  • โœ… Viewed test results in Azure DevOps
  • โœ… Saw how failing tests block deployment

Key Concepts Mastered:

  1. Unit Testing: Testing individual methods in isolation
  2. Test Frameworks: JUnit 5 for Java, xUnit for .NET
  3. AAA Pattern: Arrange, Act, Assert
  4. CI/CD Integration: Automated test execution
  5. Quality Gate: Failed tests prevent deployment

Next Steps:

  • ๐Ÿ“š Day 13: Code Coverage and Test Reporting
  • ๐Ÿงช Practice writing tests for your own code
  • ๐Ÿ“Š Aim for 80% code coverage
  • ๐Ÿ”„ Run tests before every commit
  • โœ… Keep tests fast (< 1 second each)

๐Ÿ”ง Troubleshooting Guide

Issue Solution
Maven tests not found Check test location: src\test\java
Verify test class ends with "Test"
Ensure methods have @Test annotation
.NET tests not running Check project name ends with ".Tests"
Verify [Fact] or [Theory] attributes
Run: dotnet test --list-tests
Tests pass locally, fail in pipeline Check Java/.NET version matches
Remove hardcoded paths or timestamps
Ensure tests are independent
Test results not showing in Azure DevOps Verify publishJUnitResults: true (Maven)
Verify publishTestResults: true (.NET)
Check test results file path is correct
Build passes despite test failures Remove continueOnError: true from YAML
Ensure test task doesn't have condition: always()
Check Maven/dotnet commands are correct

๐Ÿ“ Quick Reference Commands

Maven Commands:

# Run all tests mvn test # Clean and test mvn clean test # Run specific test mvn test -Dtest=CalculatorTest # Skip tests mvn package -DskipTests

.NET Commands:

# Run all tests dotnet test # Run with detailed output dotnet test --logger "console;verbosity=detailed" # Run specific test dotnet test --filter "FullyQualifiedName~CalculatorTests" # Generate test results file dotnet test --logger "trx"

Git Commands:

# Add and commit git add . git commit -m "Add tests" # Push to trigger pipeline git push

๐ŸŽ‰ Day 12 Labs Complete!

You've successfully automated testing in CI/CD pipelines!

Ready for Day 13: Code Coverage & Test Reporting

๐ŸŽ“ Skills Acquired:
  • Unit test development (JUnit & xUnit)
  • Test automation in pipelines
  • Test result analysis in Azure DevOps
  • Quality gate enforcement via tests
  • Debugging failing tests