Slide 1

Slide 1 text

@glennsarti glenn-sarti

Slide 2

Slide 2 text

@glennsarti Previously …

Slide 3

Slide 3 text

@glennsarti Unit (White box) Integration Unit (Black box) Characterisation Reduce duration Remove tests Reorder tests General maintenance

Slide 4

Slide 4 text

@glennsarti Types of tests - Integration Image adapted from Test Pyramid – Martin Fowler 1. Slower 2. More $$$ 3. Complex

Slide 5

Slide 5 text

@glennsarti Why do I test?

Slide 6

Slide 6 text

@glennsarti To reduce the risk that a user will experience an unexpected behaviour

Slide 7

Slide 7 text

@glennsarti To reduce the risk that a user will experience an unexpected behaviour

Slide 8

Slide 8 text

@glennsarti Benefits of testing Shorter delivery cycles Produce high quality code Cheaper

Slide 9

Slide 9 text

@glennsarti To ensure what is created does what it’s supposed to do Form of documentation Benefits of testing

Slide 10

Slide 10 text

@glennsarti

Slide 11

Slide 11 text

@glennsarti Image adapted from Test Pyramid – Martin Fowler

Slide 12

Slide 12 text

@glennsarti Image adapted from Test Pyramid – Martin Fowler ⚠️ Slow

Slide 13

Slide 13 text

@glennsarti Image adapted from Test Pyramid – Martin Fowler ⚠️ Expensive

Slide 14

Slide 14 text

@glennsarti Image adapted from Test Pyramid – Martin Fowler ⚠️ Complex

Slide 15

Slide 15 text

@glennsarti The cost to fix a bug Cost to Fix – Testing in Production – Rob Crowley Development Manual Testing From user Expensive Cheap

Slide 16

Slide 16 text

@glennsarti Fixing a broken test Acceptance Tests Integration Tests Unit Tests PowerShell being tested PAIN

Slide 17

Slide 17 text

@glennsarti Things that can go wrong One Plethora (*) Graph is not to scale or based on actual data Acceptance Tests Integration Tests Unit Tests

Slide 18

Slide 18 text

@glennsarti ❌ ✔

Slide 19

Slide 19 text

@glennsarti Customers don’t measure you on how hard you tried, they measure you on what you deliver.” “ - Steve Jobs

Slide 20

Slide 20 text

@glennsarti Where’s the value?

Slide 21

Slide 21 text

@glennsarti … there are known knowns … known unknowns … also unknown unknowns … it is the latter category that tend to be the difficult ones - Donald Rumsfeld “ ”

Slide 22

Slide 22 text

@glennsarti What can we do then…

Slide 23

Slide 23 text

@glennsarti Think Different Change Scope

Slide 24

Slide 24 text

@glennsarti Think Different Change Scope

Slide 25

Slide 25 text

@glennsarti Cynefin Framework “The Cynefin framework (/ˈkʌnɪvɪn/ KUN-iv-in) is a conceptual framework used to aid decision-making” - https://www.youtube.com/watch?v=N7oz366X0-8

Slide 26

Slide 26 text

@glennsarti

Slide 27

Slide 27 text

@glennsarti Complicated Complex Chaotic Obvious

Slide 28

Slide 28 text

@glennsarti Ordered Unordered Complicated Complex Chaotic Obvious

Slide 29

Slide 29 text

@glennsarti Obvious Sense – Categorise - Respond Unit Tests

Slide 30

Slide 30 text

@glennsarti Complicated Sense – Analyse - Respond Integration Tests

Slide 31

Slide 31 text

@glennsarti Complex Probe – Sense - Respond Acceptance Tests

Slide 32

Slide 32 text

@glennsarti Chaotic Act – Sense - Respond Chaos Engineering

Slide 33

Slide 33 text

@glennsarti Complicated Complex Obvious Unit Tests Integration Tests Acceptance Tests

Slide 34

Slide 34 text

@glennsarti Think Different Change Scope

Slide 35

Slide 35 text

@glennsarti ✔Reduce the scope ✔ Reduce complexity ✔ Reduce duration ✔ Reduce things that can go wrong ❌Reduces coverage

Slide 36

Slide 36 text

@glennsarti – Testing in Production – Rob Crowley

Slide 37

Slide 37 text

@glennsarti – Testing in Production – Rob Crowley

Slide 38

Slide 38 text

@glennsarti – Testing in Production – Rob Crowley

Slide 39

Slide 39 text

@glennsarti Let’s write some tests

Slide 40

Slide 40 text

@glennsarti Introducing PoshBot and Mattermost …

Slide 41

Slide 41 text

@glennsarti Mattermost Backend Mattermost Server Mattermost Client PoshBot Docker REST API PowerShell Web

Slide 42

Slide 42 text

@glennsarti Mattermost Backend Mattermost Server Mattermost Client PoshBot OAT Tests

Slide 43

Slide 43 text

@glennsarti Mattermost Backend Mattermost Server Mattermost Client PoshBot UAT Tests

Slide 44

Slide 44 text

@glennsarti Operational Acceptance Testing and Operation Validation Testing

Slide 45

Slide 45 text

@glennsarti Is the server running? Does the server think it's ok? Are the critical settings correct?

Slide 46

Slide 46 text

@glennsarti $MatterMostRoot = "http://localhost:8065" Describe "Response from ${MatterMostRoot}" { it "should return statuscode 200" { $Result = Invoke-WebRequest -Uri $MatterMostRoot -UseBasicParsing $Result.StatusCode | Should -Be 200 -Because '…’ } it "should have a title of MatterMost" { $Result = Invoke-WebRequest -Uri $MatterMostRoot -UseBasicParsing $Result.Content | Should -BeLike "*Mattermost*" } }

Slide 47

Slide 47 text

@glennsarti $MatterMostRoot = "http://localhost:8065" Describe "Response from ${MatterMostRoot}" { it "should return statuscode 200" { $Result = Invoke-WebRequest -Uri $MatterMostRoot -UseBasicParsing $Result.StatusCode | Should -Be 200 -Because '…' } it "should have a title of MatterMost" { $Result = Invoke-WebRequest -Uri $MatterMostRoot -UseBasicParsing $Result.Content | Should -BeLike "*Mattermost*" } }

Slide 48

Slide 48 text

@glennsarti

Slide 49

Slide 49 text

@glennsarti

Slide 50

Slide 50 text

@glennsarti Operation Validation Framework - https://github.com/PowerShell/Operation-Validation-Framework A set of tools for executing validation of the operation of a system. It provides a way to organize and execute Pester tests which are written to validate operation … ” “

Slide 51

Slide 51 text

@glennsarti PowerShell Module

Slide 52

Slide 52 text

@glennsarti OVF Directories

Slide 53

Slide 53 text

@glennsarti Tests

Slide 54

Slide 54 text

@glennsarti param( $MatterMostRoot = "http://localhost:8065" ) Describe "Mattermost Simple Tests" { Context "Response from $MatterMostRoot" { it "should return statuscode 200" { $Result = Invoke-WebRequest -Uri $MatterMostRoot -UseBasicParsing $Result.StatusCode | Should -Be 200 -Because '…' } it "should have a title of MatterMost" { $Result = Invoke-WebRequest -Uri $MatterMostRoot -UseBasicParsing $Result.Content | Should -BeLike "*Mattermost*" } } }

Slide 55

Slide 55 text

@glennsarti PS> Get-OperationValidation -Path OVF Module: …\MatterMostTests\OAT\OVF\ExampleModule Version: Type: Simple Tags: {} File: Mattermost-Simple.Tests.ps1 FilePath: …\Diagnostics\Simple\Mattermost-Simple.Tests.ps1 Name: Mattermost Simple Tests …

Slide 56

Slide 56 text

@glennsarti PS> Invoke-OperationValidation -Path OVF Module: C:\Source\PSSummit-Pester102\MatterMostTests\OAT\OVF\ExampleModule Result Name ------ ---- Passed Mattermost Simple Tests:Response from http://localhost:8065:should return statuscode 200 Passed Mattermost Simple Tests:Response from http://localhost:8065:should have a title of MatterMost Passed Mattermost Comprehensive Tests:Server configuration:should return statuscode 404 for V3 API Passed Mattermost Comprehensive Tests:Server configuration:should report MatterMost Server versio... Passed Mattermost Comprehensive Tests:Status endpoint at http://localhost:8065/api/v4/system/ping... Passed Mattermost Comprehensive Tests:Status endpoint at http://localhost:8065/api/v4/system/ping... Passed Mattermost Comprehensive Tests:Users API endpoint:should have more than one user

Slide 57

Slide 57 text

@glennsarti OVF Gotchas (1.2.0) Doesn’t require a PSD file Single Describe block and text only Use the newest version

Slide 58

Slide 58 text

@glennsarti Beyond OVF… Sharing Tooling ChatOps-ing - Devopsing In a Microsoft World

Slide 59

Slide 59 text

@glennsarti Compliance Acceptance Testing

Slide 60

Slide 60 text

@glennsarti

Slide 61

Slide 61 text

@glennsarti

Slide 62

Slide 62 text

@glennsarti Metadata Test

Slide 63

Slide 63 text

@glennsarti $MatterMostRoot = "http://localhost:8065" Describe "Response from ${MatterMostRoot}" { it "should return statuscode 200" { $Result = Invoke-WebRequest -Uri $MatterMostRoot -UseBasicParsing $Result.StatusCode | Should -Be 200 -Because '…’ } it "should have a title of MatterMost" { $Result = Invoke-WebRequest -Uri $MatterMostRoot -UseBasicParsing $Result.Content | Should -BeLike "*Mattermost*" } }

Slide 64

Slide 64 text

@glennsarti mattermost_port = 8065 mattermost_root = "http://localhost:#{mattermost_port}" control 'mattermost-basic-1' do impact 'critical' title 'Mattermost Server: Basic Connectivity' desc 'Basic connectivity …' tag 'mattermost', 'basic' # Basic Connectivity Tests describe port(mattermost_port) do it { should be_listening } end describe http(mattermost_root) do its('status') { should eq 200 } its('body') { should match 'Mattermost' } end end

Slide 65

Slide 65 text

@glennsarti mattermost_port = 8065 mattermost_root = "http://localhost:#{mattermost_port}" control 'mattermost-basic-1' do impact 'critical' title 'Mattermost Server: Basic Connectivity' desc 'Basic connectivity …' tag 'mattermost', 'basic' # Basic Connectivity Tests describe port(mattermost_port) do it { should be_listening } end describe http(mattermost_root) do its('status') { should eq 200 } its('body') { should match 'Mattermost' } end end

Slide 66

Slide 66 text

@glennsarti Inspec Requires an installation package $Inspec -ne $rspec Windows support can be variable

Slide 67

Slide 67 text

@glennsarti Introducing Inspecster!

Slide 68

Slide 68 text

@glennsarti control 'mattermost-basic-1' ` -impact 'critical' ` -title 'Mattermost Server: Basic Connectivity' ` -desc 'Basic connectivity tests to test …' ` -tags 'mattermost', 'basic' { describe "Response from ${MatterMostRoot}" { it "should return statuscode 200" { $Result = Invoke-WebRequest -Uri $MatterMostRoot -UseBasicParsing $Result.StatusCode | Should -Be 200 -Because '…' } …

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

@glennsarti < take a breath >

Slide 73

Slide 73 text

@glennsarti User Acceptance Testing

Slide 74

Slide 74 text

@glennsarti Mattermost Backend Mattermost Server PoshBot UAT Tests

Slide 75

Slide 75 text

@glennsarti UAT Create systems under test Run tests Destroy systems under test 1 2 3

Slide 76

Slide 76 text

@glennsarti Run docker image Configure the Mattermost server Create token for the Bot Run PoshBot as a different process Wait for Poshbot to be ready

Slide 77

Slide 77 text

@glennsarti 1

Slide 78

Slide 78 text

@glennsarti

Slide 79

Slide 79 text

@glennsarti it "should respond to '! help'" { $userInfo = Get-UserInformation # Post a Message $body = @{ 'channel_id' = $userInfo.channelid 'message' = "! help" } $originalMessage = Invoke-MattermostAPI -Uri 'posts' -Method POST -Body $body # Give the bot time to respond Start-Sleep 2 $updatedMessage = Invoke-MattermostAPI -Uri "posts/$($originalMessage.id)" ` -Method GET # The Bot should have a reaction on the message $updatedMessage.metadata.reactions.Count | Should -BeGreaterThan 0 $updatedMessage.metadata.reactions[0].emoji_name | Should -Be 'white_check_mark' $newMessages = Invoke-MattermostAPI ` -Uri "channels/$($userInfo.channelid)/posts?after=$($originalMessage.id)" ` -Method GET

Slide 80

Slide 80 text

@glennsarti it "should respond to '! help'" { $userInfo = Get-UserInformation # Post a Message $body = @{ 'channel_id' = $userInfo.channelid 'message' = "! help" } $originalMessage = Invoke-MattermostAPI -Uri 'posts' -Method POST -Body $body # Give the bot time to respond Start-Sleep 2 $updatedMessage = Invoke-MattermostAPI -Uri "posts/$($originalMessage.id)" ` -Method GET # The Bot should have a reaction on the message $updatedMessage.metadata.reactions.Count | Should -BeGreaterThan 0 $updatedMessage.metadata.reactions[0].emoji_name | Should -Be 'white_check_mark' $newMessages = Invoke-MattermostAPI ` -Uri "channels/$($userInfo.channelid)/posts?after=$($originalMessage.id)" ` -Method GET

Slide 81

Slide 81 text

@glennsarti it "should respond to '! help'" { $userInfo = Get-UserInformation # Post a Message $body = @{ 'channel_id' = $userInfo.channelid 'message' = "! help" } $originalMessage = Invoke-MattermostAPI -Uri 'posts' -Method POST -Body $body # Give the bot time to respond Start-Sleep 2 $updatedMessage = Invoke-MattermostAPI -Uri "posts/$($originalMessage.id)" ` -Method GET # The Bot should have a reaction on the message $updatedMessage.metadata.reactions.Count | Should -BeGreaterThan 0 $updatedMessage.metadata.reactions[0].emoji_name | Should -Be 'white_check_mark’ $newMessages = Invoke-MattermostAPI ` -Uri "channels/$($userInfo.channelid)/posts?after=$($originalMessage.id)" ` -Method GET

Slide 82

Slide 82 text

@glennsarti it "should respond to '! help'" { $userInfo = Get-UserInformation # Post a Message $body = @{ 'channel_id' = $userInfo.channelid 'message' = "! help" } $originalMessage = Invoke-MattermostAPI -Uri 'posts' -Method POST -Body $body # Give the bot time to respond Start-Sleep 2 $updatedMessage = Invoke-MattermostAPI -Uri "posts/$($originalMessage.id)" ` -Method GET # The Bot should have a reaction on the message $updatedMessage.metadata.reactions.Count | Should -BeGreaterThan 0 $updatedMessage.metadata.reactions[0].emoji_name | Should -Be 'white_check_mark' $newMessages = Invoke-MattermostAPI ` -Uri "channels/$($userInfo.channelid)/posts?after=$($originalMessage.id)" ` -Method GET

Slide 83

Slide 83 text

@glennsarti # The Bot should have a reaction on the message $updatedMessage.metadata.reactions.Count | Should -BeGreaterThan 0 $updatedMessage.metadata.reactions[0].emoji_name | Should -Be 'white_check_mark' $newMessages = Invoke-MattermostAPI ` -Uri "channels/$($userInfo.channelid)/posts?after=$($originalMessage.id)" ` -Method GET # There should only be one new message $newMessages.order.Count | Should -Be 1 $newPostID = $newMessages.order[0] $newPost = $newMessages.posts."$newPostID" # The message text should start with... $newPost.message | Should -Match "```````nFullCommandName" }

Slide 84

Slide 84 text

@glennsarti # The Bot should have a reaction on the message $updatedMessage.metadata.reactions.Count | Should -BeGreaterThan 0 $updatedMessage.metadata.reactions[0].emoji_name | Should -Be 'white_check_mark' $newMessages = Invoke-MattermostAPI ` -Uri "channels/$($userInfo.channelid)/posts?after=$($originalMessage.id)" ` -Method GET # There should only be one new message $newMessages.order.Count | Should -Be 1 $newPostID = $newMessages.order[0] $newPost = $newMessages.posts."$newPostID" # The message text should start with... $newPost.message | Should -Match "```````nFullCommandName" }

Slide 85

Slide 85 text

@glennsarti

Slide 86

Slide 86 text

@glennsarti Stop and remove the container Stop the Poshbot Process

Slide 87

Slide 87 text

@glennsarti try { & .\Pre-Pester.ps1 # Add other options e.g. Output file and format $result = Invoke-Pester -Script $PSScriptRoot –PassThru } finally { & .\Post-Pester.ps1 } # Return the number of failed tests. Exit Code 0 = Success $host.SetShouldExit($result.failedcount) 1 2 3 4

Slide 88

Slide 88 text

@glennsarti Thinking more different-er …

Slide 89

Slide 89 text

@glennsarti Gherkin

Slide 90

Slide 90 text

@glennsarti it "should respond to '! help'" { $userInfo = Get-UserInformation # Post a Message $body = @{ 'channel_id' = $userInfo.channelid 'message' = "! help" } $originalMessage = Invoke-MattermostAPI -Uri 'posts' -Method POST -Body $body # Give the bot time to respond Start-Sleep 2 $updatedMessage = Invoke-MattermostAPI -Uri "posts/$($originalMessage.id)" ` -Method GET # The Bot should have a reaction on the message $updatedMessage.metadata.reactions.Count | Should -BeGreaterThan 0 $updatedMessage.metadata.reactions[0].emoji_name | Should -Be 'white_check_mark' $newMessages = Invoke-MattermostAPI ` -Uri "channels/$($userInfo.channelid)/posts?after=$($originalMessage.id)" ` -Method GET # The Bot should have a reaction on the message $updatedMessage.metadata.reactions.Count | Should -BeGreaterThan 0 $updatedMessage.metadata.reactions[0].emoji_name | Should -Be 'white_check_mark' $newMessages = Invoke-MattermostAPI ` -Uri "channels/$($userInfo.channelid)/posts?after=$($originalMessage.id)" ` -Method GET # There should only be one new message $newMessages.order.Count | Should -Be 1 $newPostID = $newMessages.order[0] $newPost = $newMessages.posts."$newPostID" # The message text should start with... $newPost.message | Should -Match "```````nFullCommandName" }

Slide 91

Slide 91 text

@glennsarti Feature: It can respond to help messages Background: Login to Mattermost Given mattermost server http://localhost:8065 And mattermost user '[email protected]' with passsword 'Password1' Scenario: Sending a valid help message When sending a message of ! help And waiting 2 seconds Then the message should have a reaction of white_check_mark And poshbot returns a message that starts with ```````nFullCommandName

Slide 92

Slide 92 text

@glennsarti Feature: It can respond to help messages Background: Login to Mattermost Given mattermost server http://localhost:8065 And mattermost user '[email protected]' with passsword 'Password1' Scenario: Sending a valid help message When sending a message of ! help And waiting 2 seconds Then the message should have a reaction of white_check_mark And poshbot returns a message that starts with ```````nFullCommandName

Slide 93

Slide 93 text

@glennsarti Feature: It can respond to help messages Background: Login to Mattermost Given mattermost server http://localhost:8065 And mattermost user '[email protected]' with passsword 'Password1' Scenario: Sending a valid help message When sending a message of ! help And waiting 5 seconds Then the message should have a reaction of white_check_mark And poshbot returns a message that starts with ```````nFullCommandName

Slide 94

Slide 94 text

@glennsarti Scenario: Sending a help message with an unknown command When sending a message of ! help commanddoesntexist And waiting 2 seconds Then the message should have a reaction of white_check_mark And poshbot returns a message that contains No commands found

Slide 95

Slide 95 text

@glennsarti Tiering

Slide 96

Slide 96 text

@glennsarti Instrumentation

Slide 97

Slide 97 text

@glennsarti $Local -eq $Automated

Slide 98

Slide 98 text

@glennsarti

Slide 99

Slide 99 text

@glennsarti

Slide 100

Slide 100 text

THANK YOU! Please use the event app or Socio to submit a session rating! @glennsarti http://glennsarti.github.io/ https://speakerdeck.com/glennsarti https://github.com/glennsarti/PSSummitNA2019-Pester102

Slide 101

Slide 101 text

@glennsarti Resources Source Code https://github.com/glennsarti/PSSummitNA2019-Pester102 Cynefin https://www.youtube.com/watch?v=N7oz366X0-8 https://en.wikipedia.org/wiki/Cynefin_framework#cite_note-SnowdenBoone2007-1 http://cognitive-edge.com/videos/cynefin-framework-introduction/ Rob Crowley – Testing in production https://speakerdeck.com/robcrowley/testing-in-production-for-the-bold-and-true Mattermost https://api.mattermost.com/ Images https:/ /unsplash.com

Slide 102

Slide 102 text

@glennsarti Resources Operation Validation Framework https://github.com/PowerShell/Operation-Validation-Framework Devopsing In a Microsoft World https://youtu.be/roFVOnRxXns?t=1500 https://www.slideshare.net/snasello1/devops-days-seattle-2017 InSpec https://www.inspec.io/

Slide 103

Slide 103 text

@glennsarti Resources Gherkin https://docs.cucumber.io/gherkin/ https://powershellexplained.com/2017-03-17-Powershell-Gherkin-specification-validation/ https://powershellexplained.com/2017-04-30-Powershell-Gherkin-advanced-features/ https://www.youtube.com/watch?v=hCfcLDLTf6g Part 1 https://www.youtube.com/watch?v=mWFkFU4Hi_g Part 2 https://github.com/mikejonestechno/devops/tree/master/powershell/demo-bdd