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
Test Driven Development & Pair Programing
გიორგი მამალაძე
George Mamaladze
Software Architect
Siemens I IA AS CTO
@gmamaladze
დამიწერია და გამიშვია ავტომატიზირებული
ტესტები.
ჩემს მიერ დაწერილი კოდის უმეტესობა დაფარულია ავტომატიზირებული
ტესტებით.
ტესეტების წერაზე დროს არ ვხარჯავ.
მირჩევნია კოდის წერა.
The Five Orders of Ignorance
არცოდნის 5 ხარისხი
0th Order Ignorance
რაიმეს ობიექტურად და დანამდვილებით ცოდნა
The Five Orders of Ignorance
არცოდნის 5 ხარისხი
0th Order Ignorance
რაიმეს ობიექტურად და დანამდვილებით ცოდნა
1st Order Ignorance
როდესაც იცი რომ არ იცი
The Five Orders of Ignorance
არცოდნის 5 ხარისხი
0th Order Ignorance
რაიმეს ობიექტურად და დანამდვილებით ცოდნა
1st Order Ignorance
როდესაც იცი რომ არ იცი
2nd Order Ignorance
როდესაც არ იცი რომ არ იცი
The Five Orders of Ignorance
არცოდნის 5 ხარისხი
0th Order Ignorance
რაიმეს ობიექტურად და დანამდვილებით ცოდნა
1st Order Ignorance
როდესაც იცი რომ არ იცი
2nd Order Ignorance
როდესაც არ იცი რომ არ იცი
3rd Order Ignorance
როდესაც ისიც კი არ იცი როგორ დაადგინო რომ არ იცი
The Five Orders of Ignorance
არცოდნის 5 ხარისხი
4th Order Ignorance
მაშინ როდესაც სარეთოდ წარმოდგენა არ გაქვს
Five Orders of Ignorance-ის
არცოდნის 5 ხარისხის არსებობის შესახებაც კი.
• ტესტირების ევოლუცია
• ნამდვილი Unit ტესტი
• TDD & PP
• ტესტის აგებულება და ენა
საკითხები
Q: რა ფუნქცია აქვთ ტესტებს?
A: შეცდომების (Bug-ების) პოვნა.
ტესტი დებაგერის სანაცვლოდ
ტესტირების ევოლუცია
ტესტირების გამოყოფა
მენეჯერი
პროგრამისტები
Coding
Testing
ტესტერები
ტესტირების ევოლუცია
1. Automate framework pretends to be a user
Scenario Tests
Test Driver Application
Action
Reaction
Scenario Tests
Test Driver Application SUT
Stimulus
Asserts
• Expectation 1 R
• Expectation 2 R
• Expectation 3 S
Test Result
ტესტირების ევოლუცია
1. Automate framework pretends to be a user
slow / flaky / corner cases not tested
Code Path Coverage
public void DoSomething() { readInput(); processData(); if (!isDiskFull()) { writeOutput(); } else { showError(); }; }
public void DoSomething() { readInput(); processData(); if (!isDiskFull()) { writeOutput(); } else { showError(); }; }
Happy Path
Corner Case
Code Path Coverage
ტესტირების ევოლუცია
1. Automate framework pretends to be a user
slow / flaky / corner cases not tested
2. Break down simulate dependencies
Test Driver
Break Down
Application
Step 1
Replace user with robot
Application
Data Simulator
Test Driver
Functional Tests
Step 1
Replace user with robot
Step 2
Break down and simulate
Presentation
Business Logic
Data Layer
ტესტირების ევოლუცია
1. Automate framework pretends to be a user
slow / flaky / corner cases not tested
2. Break down simulate dependencies
much faster / can simulate
ტესტირების ევოლუცია
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
ტესტირების ევოლუცია
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
ტესტირების ევოლუცია
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
ტესტირების ევოლუცია
Scenario Tests
Functional Tests
Unit Tests
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
ტესტირების ევოლუცია
ნუთუ არასაკმარისია მხოლოდ ერთი სახის
ტესტი?
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
?????
1/7 1/5 1/11 1/13
0,14 0,20 0,09 0,07 ≈ 0,51
1/2
ნამდვილი Unit test-ი
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:
რა პრობლემებთანაა დაკავშირებული Unit-
Test-ების შექმნა?
Test Driver Class Under Test
Stimulus
Asserts
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.
Test Driver
Class Under Test
Other Class
Other Class
Other Class
Object Instantiated
Object Passed In
Global Object
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
Test Driver
Class Under Test
Other Class
Other Class
Other Class
Friendly
Friendly
Friendly
Seam
Object Instantiated
Object Passed In
Global Object
Test Driver
Class Under Test
Object Instantiated
Object Passed In
Global Object
Friendly
Friendly
Friendly
Seam
Business Logic
Object Graph Construction &
Lookup
Test Driver
Class Under Test
Object Instantiated
Object Passed In
Global Object
Friendly
Friendly
Friendly
Seam
Test Driver
Class Under Test
Mock
Stub
Fake
http://www.martinfowler.com/bliki/TestDouble.html
Test
Doubles
JUnit
Code
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
TDD & PP
What is TDD?
TDD Katas
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
using NUnit.Framework; [TestFixture] public class FizzBuzzTests { }
[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(); } }
[Test] public void TranslateOne() { string result = Translator.Translate(1); Assert.That(result, Is.EqualTo("1")); }
public static string Translate(int i) { return "1"; }
[Test] public void TranslateTwo() { string result = Translator.Translate(2); Assert.That(result, Is.EqualTo("2")); }
public static string Translate(int i) { return i.ToString(); }
[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(); }
[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(); }
[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(); }
[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(); }
[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(); }
[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(); }
[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(); }
[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(); }
[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(); }
[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(); }
[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; }
[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; }
[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; }
[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); }
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")]
[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; }
[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; }
TDD & PP
write failing test
write code to pass consolidate
TDD & PP
write failing test
write code to pass consolidate
TDD & PP
write failing test
write code to pass consolidate
TDD & PP
write failing test
write code to pass consolidate
TDD & PP
write failing test
write code to pass consolidate
ტესტის აგებულება და ენა
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
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); }
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
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?
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)
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
Use expressive names for your tets.
EXTREMELY EXPRESSIVE
Create your Test Language