84
Test Driven Development & Pair Programing გიორგი მამალაძე

Hack@macs 2014 test driven development & pair programing

  • Upload
    unihack

  • View
    187

  • Download
    1

Embed Size (px)

DESCRIPTION

გიორგი მამალაძე - Test Driven Development & Pair Programming video: https://www.youtube.com/watch?v=yGg97JeyxWE

Citation preview

Page 1: Hack@macs 2014 test driven development & pair programing

Test Driven Development & Pair Programing

გიორგი მამალაძე

Page 2: Hack@macs 2014 test driven development & pair programing

George Mamaladze

Software Architect

Siemens I IA AS CTO

@gmamaladze

Page 3: Hack@macs 2014 test driven development & pair programing

დამიწერია და გამიშვია ავტომატიზირებული

ტესტები.

Page 4: Hack@macs 2014 test driven development & pair programing

ჩემს მიერ დაწერილი კოდის უმეტესობა დაფარულია ავტომატიზირებული

ტესტებით.

Page 5: Hack@macs 2014 test driven development & pair programing

ტესეტების წერაზე დროს არ ვხარჯავ.

მირჩევნია კოდის წერა.

Page 6: Hack@macs 2014 test driven development & pair programing

The Five Orders of Ignorance

არცოდნის 5 ხარისხი

0th Order Ignorance

რაიმეს ობიექტურად და დანამდვილებით ცოდნა

Page 7: Hack@macs 2014 test driven development & pair programing

The Five Orders of Ignorance

არცოდნის 5 ხარისხი

0th Order Ignorance

რაიმეს ობიექტურად და დანამდვილებით ცოდნა

1st Order Ignorance

როდესაც იცი რომ არ იცი

Page 8: Hack@macs 2014 test driven development & pair programing

The Five Orders of Ignorance

არცოდნის 5 ხარისხი

0th Order Ignorance

რაიმეს ობიექტურად და დანამდვილებით ცოდნა

1st Order Ignorance

როდესაც იცი რომ არ იცი

2nd Order Ignorance

როდესაც არ იცი რომ არ იცი

Page 9: Hack@macs 2014 test driven development & pair programing

The Five Orders of Ignorance

არცოდნის 5 ხარისხი

0th Order Ignorance

რაიმეს ობიექტურად და დანამდვილებით ცოდნა

1st Order Ignorance

როდესაც იცი რომ არ იცი

2nd Order Ignorance

როდესაც არ იცი რომ არ იცი

3rd Order Ignorance

როდესაც ისიც კი არ იცი როგორ დაადგინო რომ არ იცი

Page 10: Hack@macs 2014 test driven development & pair programing

The Five Orders of Ignorance

არცოდნის 5 ხარისხი

4th Order Ignorance

მაშინ როდესაც სარეთოდ წარმოდგენა არ გაქვს

Five Orders of Ignorance-ის

არცოდნის 5 ხარისხის არსებობის შესახებაც კი.

Page 11: Hack@macs 2014 test driven development & pair programing

• ტესტირების ევოლუცია

• ნამდვილი Unit ტესტი

• TDD & PP

• ტესტის აგებულება და ენა

საკითხები

Page 12: Hack@macs 2014 test driven development & pair programing

Q: რა ფუნქცია აქვთ ტესტებს?

A: შეცდომების (Bug-ების) პოვნა.

Page 13: Hack@macs 2014 test driven development & pair programing

ტესტი დებაგერის სანაცვლოდ

Page 14: Hack@macs 2014 test driven development & pair programing

ტესტირების ევოლუცია

Page 15: Hack@macs 2014 test driven development & pair programing

ტესტირების გამოყოფა

მენეჯერი

პროგრამისტები

Coding

Testing

ტესტერები

Page 16: Hack@macs 2014 test driven development & pair programing

ტესტირების ევოლუცია

1. Automate framework pretends to be a user

Page 17: Hack@macs 2014 test driven development & pair programing

Scenario Tests

Test Driver Application

Action

Reaction

Page 18: Hack@macs 2014 test driven development & pair programing

Scenario Tests

Test Driver Application SUT

Stimulus

Asserts

• Expectation 1 R

• Expectation 2 R

• Expectation 3 S

Test Result

Page 19: Hack@macs 2014 test driven development & pair programing

ტესტირების ევოლუცია

1. Automate framework pretends to be a user

slow / flaky / corner cases not tested

Page 20: Hack@macs 2014 test driven development & pair programing

Code Path Coverage

public void DoSomething() { readInput(); processData(); if (!isDiskFull()) { writeOutput(); } else { showError(); }; }

Page 21: Hack@macs 2014 test driven development & pair programing

public void DoSomething() { readInput(); processData(); if (!isDiskFull()) { writeOutput(); } else { showError(); }; }

Happy Path

Corner Case

Code Path Coverage

Page 22: Hack@macs 2014 test driven development & pair programing

ტესტირების ევოლუცია

1. Automate framework pretends to be a user

slow / flaky / corner cases not tested

2. Break down simulate dependencies

Page 23: Hack@macs 2014 test driven development & pair programing

Test Driver

Break Down

Application

Step 1

Replace user with robot

Page 24: Hack@macs 2014 test driven development & pair programing

Application

Data Simulator

Test Driver

Functional Tests

Step 1

Replace user with robot

Step 2

Break down and simulate

Presentation

Business Logic

Data Layer

Page 25: Hack@macs 2014 test driven development & pair programing

ტესტირების ევოლუცია

1. Automate framework pretends to be a user

slow / flaky / corner cases not tested

2. Break down simulate dependencies

much faster / can simulate

Page 26: Hack@macs 2014 test driven development & pair programing

ტესტირების ევოლუცია

1. Automate framework pretends to be a user

slow / flaky / corner cases not tested

2. Break down simulate dependencies

much faster / can simulate

still need debugging

Page 27: Hack@macs 2014 test driven development & pair programing

ტესტირების ევოლუცია

1. Automate framework pretends to be a user

slow / flaky / corner cases not tested

2. Break down simulate dependencies

much faster / can simulate

still need debugging

3. Break down even more

Page 28: Hack@macs 2014 test driven development & pair programing

ტესტირების ევოლუცია

1. Automate framework pretends to be a user

slow / flaky / corner cases not tested

2. Break down simulate dependencies

much faster / can simulate

still need debugging

3. Break down even more

Page 29: Hack@macs 2014 test driven development & pair programing

ტესტირების ევოლუცია

Scenario Tests

Functional Tests

Unit Tests

Page 30: Hack@macs 2014 test driven development & pair programing

Scenario Tests

Unit Tests

Functional Tests

# of Tests

Execution Time

Test individual classes / methods

in isolation

Test collections of classes as subsystems

Tests the whole system pretending to be a user

More: TestPyramid

ტესტირების ევოლუცია

Page 31: Hack@macs 2014 test driven development & pair programing

ნუთუ არასაკმარისია მხოლოდ ერთი სახის

ტესტი?

Page 32: Hack@macs 2014 test driven development & pair programing

Why should I write a unit test if

we still need to have the functional one?

Test Probability of deffect

Cost To Run Action At Fault

Flashlight 50 ???

Light Bulb 1 from 5 1 Replace

Battery 1 from7 1 Replace

Switch 1 from11 1 Replace

Wiring 1 from 13 2 Replace

?????

Page 33: Hack@macs 2014 test driven development & pair programing

1/7 1/5 1/11 1/13

0,14 0,20 0,09 0,07 ≈ 0,51

1/2

Page 34: Hack@macs 2014 test driven development & pair programing

ნამდვილი Unit test-ი

Page 35: Hack@macs 2014 test driven development & pair programing

JUnit / NUnit ! = unit test

The „real“ Unit Tests

Tests that do these things aren't bad. Often they are worth

writing, and they can be written in a unit test harness.

However, it is important to be able to separate them from true

unit tests so that we can keep a set of tests that we can run

fast whenever we make our changes. Michael Feathers, "A Set of Unit Testing Rules"

•It talks to the database

•It communicates across the network

•It touches the file system

•You have to do special things to your

environment (such as editing config files) to run it.

•It runs longer than 0.1 second

•It can't run at the same time as any of your other

unit tests

A test is not a unit test if:

Page 36: Hack@macs 2014 test driven development & pair programing

რა პრობლემებთანაა დაკავშირებული Unit-

Test-ების შექმნა?

Page 37: Hack@macs 2014 test driven development & pair programing

Test Driver Class Under Test

Stimulus

Asserts

Page 38: Hack@macs 2014 test driven development & pair programing

Test Driver Class Under Test

Stimulus

Asserts

At the beginning you will realize

real unit testing is hard!

Your classes must be designed

to be testable.

Page 39: Hack@macs 2014 test driven development & pair programing

Test Driver

Class Under Test

Other Class

Other Class

Other Class

Object Instantiated

Object Passed In

Global Object

Page 40: Hack@macs 2014 test driven development & pair programing

Test Driver

Class Under Test

Other Class

Other Class

Other Class

CPU Intensive

Other Class

Other Class

Destructive operation

Other Servers

File System

Other Class

Object Instantiated

Object Passed In

Global Object

Seam

Page 41: Hack@macs 2014 test driven development & pair programing

Test Driver

Class Under Test

Other Class

Other Class

Other Class

Friendly

Friendly

Friendly

Seam

Object Instantiated

Object Passed In

Global Object

Page 42: Hack@macs 2014 test driven development & pair programing

Test Driver

Class Under Test

Object Instantiated

Object Passed In

Global Object

Friendly

Friendly

Friendly

Seam

Page 43: Hack@macs 2014 test driven development & pair programing

Business Logic

Object Graph Construction &

Lookup

Page 44: Hack@macs 2014 test driven development & pair programing

Test Driver

Class Under Test

Object Instantiated

Object Passed In

Global Object

Friendly

Friendly

Friendly

Seam

Page 45: Hack@macs 2014 test driven development & pair programing

Test Driver

Class Under Test

Mock

Stub

Fake

http://www.martinfowler.com/bliki/TestDouble.html

Test

Doubles

JUnit

Code

Page 46: Hack@macs 2014 test driven development & pair programing

Test Doubles

•Dummy objects are passed around but never actually used.

•Fake objects actually have working implementations, but usually

take some shortcut.

•Stubs provide canned answers to calls made during the test.

•Spies are stubs that also record some information based on how

they were called.

•Mocks are pre-programmed with expectations which form a

specification of the calls they are expected to receive.

http://www.martinfowler.com/bliki/TestDouble.html

Page 47: Hack@macs 2014 test driven development & pair programing

TDD & PP

Page 48: Hack@macs 2014 test driven development & pair programing

What is TDD?

Page 49: Hack@macs 2014 test driven development & pair programing

TDD Katas

Page 50: Hack@macs 2014 test driven development & pair programing

1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, Fizz Buzz, 16, 17, Fizz, 19, Buzz, Fizz,

5 3

5

3

Fizz Buzz

Page 51: Hack@macs 2014 test driven development & pair programing

using NUnit.Framework; [TestFixture] public class FizzBuzzTests { }

Page 52: Hack@macs 2014 test driven development & pair programing

[Test] public void TranslateOne() { string result = Translator.Translate(1); Assert.That(result, Is.EqualTo("1")); }

public class Translator { public static string Translate(int i) { throw new NotImplementedException(); } }

Page 53: Hack@macs 2014 test driven development & pair programing

[Test] public void TranslateOne() { string result = Translator.Translate(1); Assert.That(result, Is.EqualTo("1")); }

public static string Translate(int i) { return "1"; }

Page 54: Hack@macs 2014 test driven development & pair programing

[Test] public void TranslateTwo() { string result = Translator.Translate(2); Assert.That(result, Is.EqualTo("2")); }

public static string Translate(int i) { return i.ToString(); }

Page 55: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] public void Translate(int input, string expected) { string result = Translator.Translate(input); Assert.That(result, Is.EqualTo(expected)); }

public static string Translate(int i) { return i.ToString(); }

Page 56: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] public void Translate(int input, string expected) { string result = Translator.Translate(input); Assert.That(result, Is.EqualTo(expected)); }

public static string Translate(int i) { return i.ToString(); }

Page 57: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] public void Translate(int input, string expected) { string result = Translator.Translate(input); Assert.That(result, Is.EqualTo(expected)); }

public static string Translate(int i) { return i.ToString(); }

Page 58: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] public void Translate(int input, string expected) { string result = Translator.Translate(input); Assert.That(result, Is.EqualTo(expected)); }

public static string Translate(int i) { if (i == 3) return "Fizz"; return i.ToString(); }

Page 59: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] [TestCase(6, "Fizz")] public void Translate(int input, string expected) { string result = Translator.Translate(input); Assert.That(result, Is.EqualTo(expected)); }

public static string Translate(int i) { if (i == 3) return "Fizz"; return i.ToString(); }

Page 60: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] [TestCase(6, "Fizz")] public void Translate(int input, string expected) { string result = Translator.Translate(input); Assert.That(result, Is.EqualTo(expected)); }

public static string Translate(int i) { if (i % 3 == 0) return "Fizz"; return i.ToString(); }

Page 61: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] [TestCase(5, "Buzz")] [TestCase(6, "Fizz")] public void Translate(int input, string expected) { string result = Translator.Translate(input); Assert.That(result, Is.EqualTo(expected)); }

public static string Translate(int i) { if (i % 3 == 0) return "Fizz"; return i.ToString(); }

Page 62: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] [TestCase(5, "Buzz")] [TestCase(6, "Fizz")] public void Translate(int input, string expected) { string result = Translator.Translate(input); Assert.That(result, Is.EqualTo(expected)); }

public static string Translate(int i) { if (i % 3 == 0) return "Fizz"; if (i == 5) return "Buzz"; return i.ToString(); }

Page 63: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] [TestCase(5, "Buzz")] [TestCase(6, "Fizz")] [TestCase(10, "Buzz")] public void Translate(int input, string expected) { string result = Translator.Translate(input); Assert.That(result, Is.EqualTo(expected)); }

public static string Translate(int i) { if (i % 3 == 0) return "Fizz"; if (i == 5) return "Buzz"; return i.ToString(); }

Page 64: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] [TestCase(5, "Buzz")] [TestCase(6, "Fizz")] [TestCase(10, "Buzz")] public void Translate(int input, string expected) { string result = Translator.Translate(input); Assert.That(result, Is.EqualTo(expected)); }

public static string Translate(int i) { if (i % 3 == 0) return "Fizz"; if (i % 5 == 0) return "Buzz"; return i.ToString(); }

Page 65: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] [TestCase(5, "Buzz")] [TestCase(6, "Fizz")] [TestCase(10, "Buzz")]

public static string Translate(int i) { if (ShouldFizz(i)) return "Fizz"; if (ShouldBuzz(i)) return "Buzz"; return i.ToString(); } private static bool ShouldBuzz(int i) { return i % 5 == 0; } private static bool ShouldFizz(int i) { return i % 3 == 0; }

Page 66: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] [TestCase(5, "Buzz")] [TestCase(6, "Fizz")] [TestCase(10, "Buzz")] [TestCase(15, "FizzBuzz")]

public static string Translate(int i) { if (ShouldFizz(i)) return "Fizz"; if (ShouldBuzz(i)) return "Buzz"; return i.ToString(); } private static bool ShouldBuzz(int i) { return i % 5 == 0; } private static bool ShouldFizz(int i) { return i % 3 == 0; }

Page 67: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] [TestCase(5, "Buzz")] [TestCase(6, "Fizz")] [TestCase(10, "Buzz")] [TestCase(15, "FizzBuzz")]

public static string Translate(int i) { string returnString = string.Empty; if (ShouldFizz(i)) returnString += "Fizz"; if (ShouldBuzz(i)) returnString += "Buzz"; if (string.IsNullOrEmpty(returnString)) { returnString = i.ToString(); } return returnString; }

Page 68: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] [TestCase(5, "Buzz")] [TestCase(6, "Fizz")] [TestCase(10, "Buzz")] [TestCase(15, "FizzBuzz")]

public static string Translate(int i) { string returnString = string.Empty; returnString = Fizzy(i, returnString); if (ShouldBuzz(i)) returnString += "Buzz"; if (string.IsNullOrEmpty(returnString)) { returnString = i.ToString(); } return returnString; } private static string Fizzy(int i, string returnString) { return returnString + (ShouldFizz(i) ? "Fizz" : string.Empty); }

Page 69: Hack@macs 2014 test driven development & pair programing

public static string Translate(int i) { string returnString = string.Empty; returnString = Fizzy(i, returnString); returnString = Buzzy(i, returnString); if (string.IsNullOrEmpty(returnString)) { returnString = i.ToString(); } return returnString; } private static string Buzzy(int i, string returnString) { return returnString + (ShouldBuzz(i) ? "Buzz" : string.Empty); }

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] [TestCase(5, "Buzz")] [TestCase(6, "Fizz")] [TestCase(10, "Buzz")] [TestCase(15, "FizzBuzz")]

Page 70: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] [TestCase(5, "Buzz")] [TestCase(6, "Fizz")] [TestCase(10, "Buzz")] [TestCase(15, "FizzBuzz")]

public static string Translate(int i) { string returnString = string.Empty; returnString = Fizzy(i, returnString); returnString = Buzzy(i, returnString); returnString = Other(i, returnString); return returnString; } private static string Other(int i, string returnString) { return string.IsNullOrEmpty(returnString) ? i.ToString() : returnString; }

Page 71: Hack@macs 2014 test driven development & pair programing

[TestCase(1, "1")] [TestCase(2, "2")] [TestCase(3, "Fizz")] [TestCase(5, "Buzz")] [TestCase(6, "Fizz")] [TestCase(10, "Buzz")] [TestCase(15, "FizzBuzz")]

public static IList<Func<int, string, string>> Rules = new List<Func<int, string, string>> { Fizzy, Buzzy, Other }; public static string Translate(int i) { string returnString = string.Empty; foreach (var rule in Rules) { returnString = rule(i, returnString); } return returnString; }

Page 72: Hack@macs 2014 test driven development & pair programing

TDD & PP

write failing test

write code to pass consolidate

Page 73: Hack@macs 2014 test driven development & pair programing

TDD & PP

write failing test

write code to pass consolidate

Page 74: Hack@macs 2014 test driven development & pair programing

TDD & PP

write failing test

write code to pass consolidate

Page 75: Hack@macs 2014 test driven development & pair programing

TDD & PP

write failing test

write code to pass consolidate

Page 76: Hack@macs 2014 test driven development & pair programing

TDD & PP

write failing test

write code to pass consolidate

Page 77: Hack@macs 2014 test driven development & pair programing

ტესტის აგებულება და ენა

Page 78: Hack@macs 2014 test driven development & pair programing

Cyclomatic complexity of 1 (no conditionals)

Containing three sections

Prepare: Declaration and initialization

Do: Do actual work – invoke the member under test.

Assert: Assert execution results.

The Structure of a Unit Test

Page 79: Hack@macs 2014 test driven development & pair programing

In ideal case every test contains exactly tree lines:

The Structure of a Unit Test

Terminology:

•Target (target)

•Source data

•Expectation (expected)

•Actual result (actual)

[Test] public void Test(...) { var target = TargetFactory.Create(sourceData); var actual = target.DoSomething(); Assert.AreEqual(expected, actual); }

Page 80: Hack@macs 2014 test driven development & pair programing

Create your Test Language

„The language you write test code is not

the language you write your productive

code.“

• Custom assertion library

• Test data factory

• Expressive names

• One assertion per test

• Cyclomatic complexity ==1

• 3 Lines rule

Page 81: Hack@macs 2014 test driven development & pair programing

Assertion

* String lenghts differ… * Expected: True but was: False Etc.

Our goal is to find an error without much debugging. Let the failed test supply us with following information: 1. Which method was tested? 2. What was the intention of test? 3. What’s gone wrong exactly?

Page 82: Hack@macs 2014 test driven development & pair programing

Assertion

Let your asserst tell you the truth!

Failed: Clear_removes_all_elements_from_collection Expected number of elements 0 but was 1

Expected True but was False at (0,0)

Page 83: Hack@macs 2014 test driven development & pair programing

Factory vs Build-up Code

Test Case Factories are classes which produce complex

test object trees based on very few simple parameters.

Simple means - simple to write & simple to read.

TreeFactory.BuildTree(„a[b1,b2,b3[c1,c2,c3]]“)

a

b1

b2

b3

c1

c2

c3

Page 84: Hack@macs 2014 test driven development & pair programing

Use expressive names for your tets.

EXTREMELY EXPRESSIVE

Create your Test Language