Title: Methodology
1Methodology
- Rigid approaches
- Prince2
- RUP
- Agile development
- XP
- Test Driven Development
- Design Driven Development
2XP
- Feedback
- Assuming simplicity
- Incremental changes (instead embracing change)
- Continois integratgion
- Incremantal development (short period)
- Refactoring
- Unit tests
- Code over documentation
- Pair programming
3Unit test example (NUnit)
4VSTS Overview members (1)
5VSTS code coverage
6Unit testing framework
- Microsoft unit framework VS2005 Team Edition or
VS2008 - integrated with VS (debugging etc.)
- generating Tests from Existing Code
- generating accessors for Private Methods,
Properties, and Fields - code coverrage
- NUnit or mbUnit
- Widely used by the community
- Several plugins for integration of NUnit or
mbUnit with VS are available
7N/MBUnit supporting tools
- Code coverage NCover, NCoverExplorer
- Analyze unit test runs to find out which parts of
application code have been tested - Generate coverage reports and statistics
- Integration
- Test Mattrix (commercial)
- TestDriven.Net (commercial)
- ReSharper (commercial)
- NUnitGen
8Some tools
- Static Analysis FXCop, Vil
- FXCop is also in VSTS, Vil provides advanced code
metrics - Mock framework
- RhinoMock or TypeMock
9Test inputs and outputs
- Direct input
- Inputs and calls to components programmed in unit
test - Direct output
- All results of programmed actions in unit test
that are directly controllable withing the test
program (ie. in form of variables) - Indirect input
- Dependencies of CUT that can influence test
results, ie. events - Indirect output
- Results of actions programmed into testing code
that are external to unit test, ie. data saved to
a file
10Unit testing typical scenarios
- Standard unit tests
- Provides references to libraries with code to be
tested - Write test fixtures testing code
- Provide assertions and expected values
- Plug tests into the overnight
- Database unit tests
- Write set-up and tear-down constructs for
initializing databases under test - Control db state with use of transactions
- Provide assertions and expected values
- Plug tests into the overnight
- WinForms / Web tests
- Record tests by clicking on forms
- Provide assertions and expected values
- Plug tests into the overnight
11Test stubs How to test before jobs done
- To turn off DOCs we use different kind of stubs,
depending on the stituation - Basic stub types
- Responder
- Write DOCs routines with hardcoded return
statements with correct values - Saboteur
- Write DOCs routines with hardcoded throw
exceptions statements or returning incorrect
values - Stubbing techniques
- Hand-coded (classic)
- Dynamically generated (mock)
12Test stub example CUT (1) Main class
- namespace Stubs
-
- public class DbAccessImpl IDbAccess
-
- public double getUserAccountStatus (strin
g user, string password, IVerify verifier) -
- if (verifier.verifyUser(user,
password)) -
- if (user.Equals("user1"))
-
- return 100.30
-
- else if (user.Equals("user2"))
-
- return 10.10
-
- else
- throw new UserAccountExceptio
n("User not found") -
13Test stub example (2) CUTInterfaces and DOCs
- public interface IDbAccess
-
- double getUserAccountStatus(string user,
string password, IVerify verifier) -
- public interface IVerify
-
- bool verifyUser(string user, string
password) -
- namespace Stubs
-
- public class VerifierImpl IVerify
-
- public bool verifyUser(string user, string
password) -
- throw new Exception("The methodor
operation is not implemented.") -
-
14Test stub example - approaches
- We are missing real implementation of IVerify
interface. - To be able to test CUT, we need to somehow
provide replacement for IVerify implementation - There are four basic approaches
- Create test for real implementation and leave it
failing - Create a responder
- Create a saboteur
- Create a mock
15Test stub example failing test
- Test
- public void DbAccessImplTest()
-
- string user "user1"
- string password "correctpassword"
- double expected 100.30
- IDbAccess idb new DbAccessImpl()
- IVerify verifier new
VerifierImpl() - double actual idb.getUserAccountSt
atus(user, password, verifier) - Assert.AreEqual(expected, actual, "It
won't work") -
16Test stub example - responder
- class VerifierResponder IVerify
-
- public bool verifyUser(string user, string
password) -
- return true
-
-
- Test
- public void DbAccessResponder()
-
- string user "user1"
- string password "correctpassword"
- double expected 100.30
- IDbAccess idb new DbAccessImpl()
- IVerify verifier new
VerifierResponder() - double actual idb.getUserAccountStat
us(user, password, verifier)
17Test stub example saboteur (1)How does our app
behave in special conditions
- class IDbAccessSabouteur1 IVerify
-
- public bool verifyUser(string user,
string password) -
- return false
-
-
- Test
- ExpectedException DbConnectionException
- public void DbAccessSaboteur()
-
- string user "user1"
- string password "correctpassword"
- double expected 100.30
- IDbAccess idb new DbAccessImpl()
- IVerify verifier new
IDbAccessSabouteur1()
18Test stub example saboteur (2)Are we catching
our exceptions?
class IDbAccessSabouteur2 IVerify
public bool
verifyUser(string user, string password)
throw new DbAccessException()
Test ExpectedException
DbConnectionException public void
DbAccessSaboteur() string
user "user1" string password
"correctpassword" double expected
100.30 IDbAccess idb new
DbAccessImpl() IVerify verifier
new IDbAccessSabouteur2() double
actual idb.getUserAccountStatus(user,
password, verifier)
Assert.AreEqual(expected, actual, will it
work?")
19Test stub example - mock
SetUp public void setup() mocks new
Mockery()
- Test
- public void DbAccessMock()
-
- string user "user1"
- string password "correctpassword"
- double expected 100.30
- IVerify aMock (IVerify)mocks.NewMock
(typeof(IVerify)) - Stub.On(aMock).Method("verifyUser")
- .With( new Object user,
password ) - .Will(Return.Value( true ))
- IDbAccess idb new DbAccessImpl()
- double actual idb.getUserAccountStat
us(user, password, aMock) - Assert.AreEqual(100.30, actual, "It
works! ") -
20Database testing
- A bit harder than standard code-only unit test
- There exists several approaches on how to test
code that depends on database layer and the
database layer itself - Fake object
- Sandboxing
- Transaction rollback
- Stored procedures test
- Table truncation teardown
21GUI testing
- Logic spagetti Events/cod/business logic
- How to test state of application
- Framereworks
22 23Unit test example (NUnit)
24NUnit Assertions (1)
- Assertions - classic model
- Standard Assert
- Assertion.Assert ( boolean condition)
- Equality
- Assert.AreEqual( object expected, object actual,
string message, params object parms )
AssertNotEqual (...) - Identity
- Assert.AreSame( object expected, object actual,
... ) - Assert.Contains( object anObject, IList
collection, ... ) - ... and similar
- Comparison
- Assert.Greater( IComparable arg1, IComparable
arg2, ... ) - ... and similar
25NUnit Assertions (2)
- Type
- Assert.IsInstanceOfType ( Type expected, object
actual, ... ) - Assert.IsAssignableFrom
- ... and similar
- Condition
- Assert.IsTrue( bool condition, ... )
- Assert.IsNull( object anObject, ... )
- Assert.IsNaN( double aDouble, ... )
- Assert.IsEmpty( ICollection collection, ... )
- ... and similar
- Utility
- Assert.Fail( ... )
- Assert.Ignore( ... )
26NUnit Assertions (3)
- String
- StringAssert.Contains (string expected, string
actual, ... ) - StringAssert.StartsWith( ...)
- ... and similar
- Collections
- CollectionAssert.AllItemsAreInstancesOfType
(Collection collection, Type expectedType ) - CollectionAssert.AllItemsAreNotNull
- CollectionAssert.AreEqual
- CollectionAssert.AreEquivalent
- CollectionAssert.IsSubsetOf
- ... and similar
- File
- FileAssert.AreEqual( Stream expected, Stream
actual ) - ... and similar
27NUnit Assertions (4)
- Assertions - constraint model
- Assert.That ( object actual, IConstraint
constraint, string message, object parms ) - public interface IConstraint bool Matches(
object actual ) void WriteMessageTo(
MessageWriter writer ) void
WriteDescriptionTo( MessageWriter writer )
void WriteActualValueTo( MessageWriter writer )
28NUnit - Attributes
- Basic
- Test
- TestFixture
- Setup
- Teardown
- Others
- Category
- Description
- Expected Exception
- Explicit
- Ignore
- Platform
- Property
- SetUpFixture
- Suite
- TestFixtureSetUp
- TestFixtureTearDown
29 30MS Unit Tests and NUnit
31MS Unit Tests and NUnit
32MS Unit Tests and NUnit
- Frameworks have a lot in common. Sometimes the
name - are even the same.
- MS Unit Test is enriched by fallowing features
- DataSource possibility of driven unit tests by
data stored in database. - TestProperty additional information about test
case passed to report generated by test runner. - Assertion available as generics. Checking type of
parameters in compilation time. - Assertions in MS Unit Test are stronger. They
required type equality i.e. Assertion( (int)1 ,
1L ) will file. - More information http//wiki/doku.php?idr_dcrui
secontrol
33MS Unit Tests Attributes
- TestMethod mark a method as a test method.
- TestClass mark a class as a test class.
- AssemblyInitialize and AssemblyCleanup mark
methods that execute before and after all of the
tests in an assembly are executed. - ClassInitialize and ClassCleanup mark
methods that execute before and after all of the
tests in a class are executed. - TestInitialize and TestCleanup mark methods
that execute before and after each test method is
executed - DeploymentItem - used to specify deployment
items such as files or directories for per-test
deployment. - DataSource - provides data source-specific
information for data-driven testing
34Attributes that provide additional inftormation
about test methods.
- These attributes are useful when you are working
with - hundreds of unit tests and you need to manage the
tests by - sorting and filtering the tests
- Owner Enables you to specify the author of a
test method - Description Enables you to provide a
description of a test method - Priority Enables you to specify an integer
priority for a test - TestProperty Enables you to specify an
arbitrary test property
35Data Driven Tests
- A data-driven unit test is a unit test that is
run repeatedly for each row in a data source. - When a data-driven unit test is running, data is
retrieved from the rows of a data source. The
data is available to the running unit test
through the DataRow and DataConnection properties
of the TestContext class. - TestContext.DataRow"LastName"
36Data Driven Tests
- namespace TestProject1
- TestClass
- public class TestClass
-
- private TestContext m_testContext
- public TestContext TestContext
-
- get return m_testContext
- set m_testContext value
-
- TestMethod
- DeploymentItem("FPNWIND.MDB")
- DataSource("System.Data.OleDb",
"ProviderMicrosoft.Jet.OLEDB.4.0 - DataSource\"FPNWIND.MDB\"",
"Employees", DataAccessMethod.Sequential) - public void TestMethod()
-
- Console.WriteLine( "EmployeeID 0,
LastName 1", - TestContext.DataRow"EmployeeID",
TestContext.DataRow"LastName" ) -
37UT in practice
38UnitTests
- Isolation
- Speed
- Self containment
- Race safe
- Independence
- Well documented
- Maintainable
39Some Patterns
- Simple test
- Exception
- Testing protected methods/properties, overriding
methods - Reflection usage
- Mock usage
40Pattern 1 4 Stage Testing
- When to use
- This is the normal testing scheme.
- How to use
- TestFixture per tested class
- Setup prerequisite Objects - Create the scenario
of the test, this can be done in the test method
or in the SetUp and TestFixtureSetUp methods - Call the method being tested
- Valuate the results
- Teardown the Objects
41Very simple class
- public class Authentication
- private string _key public string Key
get return _key set _key
value public string EncodePassword(string
password) // do the encoding return
encoded_password -
42... and very simple test
- TestFixture
- public class TestFixture1
- Authentication authenticator String
name SetUp public void Init() // se
t up our authenticator and key authenticator
new Authentication() authenticator.Key
"TESTKEY" name "user" - ...
43... and very simple test
- TearDown public void End()
- // finish tests authenticator.Key ""
-
- Test public void Encoding ()
- // continue specific test set up String
expectedKey "fwe94t-gft5" // Call our
method String recivedKey authenticator.Enco
dePassword(name) // Validate that for "user"
and "TESTKEY" - //key we should get our key Assert.AreEqual(exp
ectedKey,recivedKey)
44Pattern 2 Test Exception
- When to use
- When we are expect our test to raise an
exception - How to use
- Use nUnit's ExpectedException attribute
45Exceptions are not exceptional
- public string EncodePassword(string password)
- if (passwordnull password.Length0)
throw new ValidationException ("Passwor
d is empty") - // do the encodingreturn encoded_password
-
46Exceptions are not exceptional
- Test
- ExpectedException(ValidationException, "Pass
word is empty") - public void Encoding ()
- authenticator.EncodePassword(null)
-
47Pattern 3 Inner Class
- When to use
- When a protected entity (field or method), needs
to be accessed for the test. We need to - test a protected method or access a protected
field. - override a public or protected (virtual) method
- How to use
- Create a new class that extends our tested class
- To test a protected method, add a method in our
extended class that calls the protected method
(Delegation). - To override a method, override this method in our
extended class.
48protected string EncodePassword
- class TestAuthentication Authentication
- public string CallEncodePassword(string
password) // We can call a protected
method from here return EncodePassword(passw
ord) -
- Test public void Encoding ()
- TestAuthentication authenticator
- new TestAuthentication()authenticator.Key
"TESTKEY"String expectedKey
"fwe94t_at_5"String name "user"// call the
tested method by means of the Inner Class - Assert.AreEqual(expectedKey,authenticator.
- CallEncodePassword(name))
-
49override virtual int DoSelect
- override a public or protected (virtual) method
- public bool IsAuthenticated(string name,string
password) - // do somethingint Results
DoSelect(statement)return Results1 -
- protected virtual int DoSelect(string statement)
- // do something
-
50- Test public void Authenticated ()
- TestAuthentication authenticator new
TestAuthentication() // if 1 record found
should be authenticated authenticator.returnAmoun
t 1 Assert.IsTrue(authenticator.
IsAuthenticated("user","password")) -
- // if no records found should not // be
authenticated authenticator.returnAmount
0 Assert.IsFalse(authenticator. IsAuthenticate
d("user","password")) -
51Patern 4 Reflection Test
- When to use
- When we need to test private methods use the
reflection test pattern. - In most cases we don't need to test private
methods. - How to use
- Use reflection.
-
52Reflection
- Test
- public void Internals ()
- Type type typeof (Authentication)Authenticat
ion authenticator (Authentication)type. - GetConstructor(System.Type.EmptyTypes).
- Invoke(null)
- bool field (bool)type.GetField("privateField,
BindingFlags.NonPublic BindingFlags.Instance
BindingFlags.Public) . GetValue(authenticator)A
ssert.IsTrue(field)bool result
(bool)type.GetMethod("PrivateMethod,..). - Invoke(authenticator ,null)Assert.IsTrue(re
sult) -
53Pattern 5 Mock usage
- When to use
- The real object has non deterministic behavior
(it produces unpredictable results, like a date
or the current weather temperature.) - The real object is difficult to set up.
- The real object has behavior that is hard to
trigger (for example, a network error). - The real object is slow.
- The real object has (or is) a user interface.
- The test needs to ask the real object about how
it was used (for example, a test might need to
confirm that a callback function was actually
called). - The real object does not yet exist (a common
problem when interfacing with other teams or new
hardware systems).
54Pattern 5 Mock usage
- How to use
- The three key steps to using mock objects for
testing are - Use an Interface to describe the object
- Implement the interface for production code
- Implement the interface in a mock object for
testing
55Mocks
MockInit Test Test MockValid
Mocked ...
CUT
56- IList subscriberpublic void AddSubscriber(ISubs
criber subscriber) this.subscriber.Add(sub
scriber)public bool IsAuthenticated(string
name, string password) foreach
(ISubscriber sub in subscriber)
sub.Recive(name) // do something
int Results DoSelect(statment) return
Results1 - public interface ISubscriber void
Receive(String message)
57NMock
- Test
- public void Subscriber()
- DynamicMock subscriber new
DynamicMock(typeof(ISubscriber)) Authentication
authenticator new Authentication() auth
enticator.Add( (ISubscriber)
subscriber.MockInstance) - // expectations subscriber.Expect("Receive",
new Object "user") //
execute Assert.IsTrue(authenticator.
IsAuthenticated("user","password")) - subscriber.Verify()
-
58Mocks which one ?
- NMock
- TypeMock
- RhinoMock
- dynamic/natural syntax ?
- what can be mocked ?
59Syntax
- dynamic
- mock.Expected(method,parameters,...)
- NMock2, TypeMock
- natural
- mock.method(parameters,...)
- TypeMock, RhinoMock
60What can be mocked
- NMock Interface
- RhinoMock interface/class (not sealed, only
virtual methods) - TypeMock
- all above
- static/non static methods of any class and
objects, creation of objects as well
61Design for testability
62Book
- Agile Principles Patterns Practices in C -
Prentice Hall.chm
63Design Smells
- Rigidity
- Fragility
- Immobility
- Viscosity
- Needless complexity
- Needless repetition
- Opacity
64Reasons
- accumulation of small departures
- drifting of the requirements ?
- hurry-up!
- fragile design ...
65Blessed Simplicity
- copy
- signs
- from keyboard
- to printer
- public class Copier
- public static void Copy()
- int c
- while((cKeyboard.Read()) ! -1)
- Printer.Write(c)
-
66Requirements changes ...
- from keyboard or tape Reader
- public class Copier
- public static bool rdFlag false
- public static void Copy()
- int c
- while((c (rdFlag ? PaperTape.Read()
- Keyboard.Read()) ! -1)
- Printer.Write(c)
-
67Requirements changes ...
- public class Copier
- public static bool rdFlag false
- public static bool ptFlag false
- public static void Copy()
- int c
- while((c (rdFlag ? PaperTape.Read()
- Keyboard.Read()) ! -1)
- if (ptFlag) PaperTape.Write(c) else
Printer.Write(c) -
68Requirements changes ...
- More sources and more destinations
- Handle I/O errors
- Copy and encode characters
- Log the contents to the file
- Change the format basing on context (upper first
letter of the sentence
69Requirements changes ...
- always
- or at least sometimes
70... be ready
- public class KeyboardReader
- public int Read() return Keyboard.Read()
-
- public class PrinterWriter
- public Write(int c) return Printer.Write(c)
-
- public class Copier
- public static KeyboardReader reader new
KeyboardReader() - public static PrinterWriter writer new
PrinterWriter() - public static void Copy()
- int c
- while((c(reader.Read())) ! -1)
- writer.Write(c)
71Changes in printer, keyboard
- Changes in printer, keyboard
- forces changes in copier
- forces recompilation of copier
- Copier developer has to know the classes
PrinterWriter, KeyboardReader - Many writer classes have to derive from printer
72Interface
- like a class but without
- implementation
- attributes
- private members
- Interface is designed to be a base
- can be used as a type of variable (when we plan
to use instances of implementing classes) - cannot be instantiated
73... for interface
- public interface Reader
- public int Read()
-
- public class KeyboardReader Reader
- public int Read() return Keyboard.Read()
-
- public class Copier
- public static Reader reader new
KeyboardReader() - public static Writer writer new
PrinterWriter() - public static void Copy()
- int c
- while((c(reader.Read())) ! -1)
- writer.Write(c)
74Enigmatic Principles
- The Single-Responsibility
- Open/Close
- Liskov Substitution
- Dependency-Inversion
- Interface Segregation
75Single-Responsibility
- A class should have only one reason to change.
- Printers are responsible for writing
- Readers are responsible for reading
76Open/Close
- Software entities (classes, modules, functions,
etc.) should be open for extension but closed for
modification - new readers/writers/formatters might be added in
easy way
77STRATEGY Pattern
78TEMPLATE METHOD pattern
79Liskov Substitution
- Subtypes must be substitutable for their base
types. - TapeReader KeyboardReader ???
- Derive means is a kind of
- Agreggation is usually better than deriving
- Deriving interfaces over deriving implementation
80Dependency-Inversion
- High-level modules should not depend on
low-level modules. Both should depend on
abstractions. - Abstractions should not depend upon details.
Details should depend upon abstractions. - Should copier deppend on reader
- Should reader depend on copier?
- ... both should depend on interfaces
81The interface segregation
- Clients should not be forced to depend on
methods they do not use. -
- Reader interface should be separate from writer
interface - Interface is more clear than a class
- Interfaces should be consistent
- Interfaces should not be to wide (WCF optimal
3-5-9)
82Interface should be (introduced)
- when
- there will be subclasses
- when the class will be exposed (modules, users,
services...) - optionally for compound parameters
- - public properties (private fields will be
defined in classes/structures)
83Some thoughts about UT?
84Potential problems
- Wide range of logic covered by single test
- Long test body
- Test bases on many aspects of CUT behavior
- The above implies
- Potentially frequent changes of test in the
future - Poor problem localization (debugging necessity)
- Complex of testing code
- Test cancer possibility
85Some observations
- Separating aspects into separate functions
reduces code necessary to initialize the unit
test and cause the test code smaller less
complicated - Short code gt short Test but on the other hand,
is necessary to test the one-line function ? - Two forms of returning of value are possible when
- MYTYPE function()
- void function(out MYTYPE value)
- 1st form is more natural, the result can be
easier set up for this version (by SetResult.For
) - Rhino mock requires- Interfaces- classes
virtual functions -
- Protected functions can be called via test
specific subclass or via reflection but private
only via reflection
86Good practices
- Meaningful names of tests
- One aspect per test.
- .. so we should avoid the multiplication of
assertions in a single test (Expect.Call is also
an assertion) - Test shouldnt contain the conditional logic,
loop etc. (how to test the test ?) - The logic of tested code shouldnt be changed for
purpose of UT introduction (esp. hacks) - Dynamic mocks over static mocks
- SetResult.For over ExpectCall
- Repeat.Times, VerifyAll, Ordered are not always
necessary
87Unit testing flavours
- State verification, Behavior verification
- Test after, test first (TDD, BDD)
- Test After
- After ? The Day, the Week After,
- After immediately after
- Always Fail the test
- Test Driven Design
- Suite of tests is a nice side effect of the
process of DESIGNING application through unit
tests.
88Lets imagine TDD?
89What we know ?
UpdateDictionaries()
listOfDictsToSync foreach (LDTS timestamp
in listOfDictsToSync)
GetOutOfSyncTimestamps
SynchronizeDictionaryForTimestamp
90- //public class When_dictionaries_are_being_updated
- Test
- public void List_of_out_of_sync_timestamps_is_to_b
e_retrieved() -
- LDTS listToReturn New.TimestampList(new
object 1, 2, 3, 4, 5, 6 ) - SetupResult.For(outOfSynctimestampsProvider.G
etOutOfSyncTimestamps()). Return(listToReturn) - mocks.ReplayAll()
- sut.UpdateDictionaries()
- Assert.That(sut.ListOfDictsToSync,
Is.EqualTo(listToReturn)) -
- Test
- public void Synchronizer_must_be_called_for_each_e
lement_on_list() -
- LDTS listToReturn New.TimestampList(new
object 1, 2)SetupResult.For(outOfSynctimest
ampsProvider.GetOutOfSyncTimestamps()). Return(li
stToReturn) - Expect.Call(synchronizer.SynchronizeDictionaryFor
Timestamp(listToReturn0)) - Expect.Call(synchronizer.SynchronizeDictionaryFor
Timestamp(listToReturn1)) - mocks.ReplayAll()
91More decisions ?
UpdateDictionaries()
listOfDictsToSync foreach (LDTS timestamp
in listOfDictsToSync)
GetOutOfSyncTimestamps
GetDictionaryValues
SynchronizeDictionaryForTimestamp
SetDictionary
92- //public class When_dictionary_is_synchronized_for
_given_timestamp - Test
- public void source_dictionary_provider_must_be_ca
lled_for_values_of\ - _dictionary_with_given_id_and_timestamp()
-
- DictionaryTimestamp timestamp New.Timestamp
- .WithId(123)
- .WithTimestamp(DateTime.Parse("08
2495843")) - DateTime actualTimestamp
- ListltListltstringgtgt contents
- Expect.Call( sourceDictionaryProvider.GetDiction
aryValues(timestamp.DictionaryId,
timestamp.Timestamp, out contents, out
actualTimestamp)) - mocks.ReplayAll()
- sut.SynchronizeDictionaryForTimestamp(timestamp)
- mocks.VerifyAll()
-
93- //public class When_dictionary_is_synchronized_for
_given_timestamp - Test
- public void destination_dictionary_with_given_id_m
ust_be_updated_with_values\ - _from_source_dictionary()
-
- DictionaryTimestamp timestamp New.Timestamp
- .WithId(123)
- .WithTimestamp(DateTime.Parse("082495843"))
- DateTime actualTimestamp
- ListltListltstringgtgt contents New.Contents
//initialize - Expect.Call( destinationDictionaryUpdater.SetDic
tionary(timestamp.DictionaryId,
timestamp.Timestamp,contents)) - mocks.ReplayAll()
- sut.SynchronizeDictionaryForTimestamp(timestamp)
- mocks.VerifyAll()
94- public class Library
-
- private readonly IOutOfSyncTimestampsProvi
der outOfSyncTimestampsProvider - private readonly IDictSynchronizer
synchronizer - protected IListltDictTimestampgt
listOfDictstionariesToSync - public Library(IOutOfSyncTimestampsProvider
outOfSyncTimestampsProvider,
IDictSynchronizer synchronizer) -
- this.outOfSyncTimestampsProvider
outOfSyncTimestampsProvider - this.synchronizer synchronizer
-
- public void UpdateDictionaries()
-
- listOfDictsToSync outOfSyncTimestamps
Provider.GetOutOfSyncTimestamps() - foreach (LDTS timestamp in
listOfDictsToSync) -
- synchronizer.SynchronizeDictionary
ForTimestamp(timestamp) -
-
95- TestFixture
- Category("LibrarySpec")
- public class When_dictionaries_are_being_updated
-
- private MockRepository mocks
- private IOutOfSyncTimestampsProvider
outOfSynctimestampsProvider - private TestSpecificLibrarySubclass sut
- private IDictionariesSynchronizer synchronizer
- SetUp
- public void SetUp()
-
- mocks new MockRepository()
- outOfSynctimestampsProvider
mocks.DynamicMockltIOutOfSyncTimestampsProvidergt
() - synchronizer mocks.DynamicMockltIDic
tionariesSynchronizergt() - sut new TestSpecificLibrarySubclass
(outOfSynctimestampsProvider, -
synchronizer) -
- public class TestSpecificLibrarySubclass
Library -
96- TestFixture
- Category("LibrarySpec")
- public class When_dictionary_is_synchronized_for_g
iven_timestamp -
- private IDictionariesSynchronizer sut
- private IDictionariesProvider sourceDictionaryPro
vider - private IDictionariesUpdater destinationDictionar
yUpdater - private MockRepository mocks
- SetUp
- public void SetUp()
-
- mocks new MockRepository()
- sourceDictionaryProvider
mocks.DynamicMockltIDictionariesProvidergt() - destinationDictionaryUpdater
mocks.DynamicMockltIDictionariesUpdatergt() - sut new DictionariesSynchronizerImp
l(sourceDictionaryProvider, destinationDictio
naryUpdater) -
97Code is important!
- Developer can sometimes get away with bad design
when there are no unit tests - just more time
will be spent on development and more bugs tester
will have to find. When unit tests are there bad
design end up in brittle unit tests that are to
big, test multiple responsibilities and break
much too often - then there are two options - fix
the design and unit tests or dump unit testing
altogether and carry on with bad design. - So - Do not write poor tests!
- Most important thing in unit testing is good OO
design. - Single responsibility principle
- Open Close principle
- Liskovs Substitution principle
- Interface segregation principle
- Dependency inversion principle
- RoleInterfaces vs wide interfaces (seggregation)
98A bit more toughts
- There are more to test doubles than just mocks!
Use test specific subclasses, fakes and stubs
(you can create some of those in Rhino Mocks) - AutoMockingContainer Rhino Mocks Windsor IoC
container - Mocks all dependencies automatically with dynamic
mocks - Tests less coupled to implementation
- One doesn't care about dependencies irrelevant to
current test - Tests might become less understandable -
something happens under the hood - Problematic when constructor contains logic under
test - we need to setup mocks and expectations
before AutoMockingContainer kicks in
99So... how to start?
- What may be the steps?
- Write tests for simple fuctions with some logic
(conditional, loops) - Short, clear and simple unit tests code (no
logic in the test, no hacks in the code, no
REPETITIONS) - Single logical assertion
- Meaningful names
- Proper ussage of Mocks
- Write tests for functions when a simple
extraction of logic is possible - Try to refactor the code (with some UT already
written) - Try to write the tests correctly for the new code
i.e. - test before or immediatelly after
100Refactoring ?
101Contents
- First rendezvous
- TDD and continues refactoring
- Why should you refactor?
- When should you refactor?
- Strategy
- Bad smells in code
- Low level refactoring technics
- Refactoring to design patterns
102First rendezvous
- Refactoring a change made to the internal
structure of software to make it easier to
understand and cheaper to modify without changing
its observable behavior.
103A bit of propaganda
- Programs that are hard to read are hard to
modify. - Programs that have duplicated logic are hard to
modify. - Programs that require additional behavior that
requires you to change running code are hard to
modify. - Programs with complex conditional logic are hard
to modify. - Any fool can write code that a computer can
understand. Good programmers write code that
humans can understand.
104TDD and continues refactoring
- RedYou create a test that expresses what you
expect your code to do. The test fails (turns
red) because you haven't created code to make the
test pass. - Green You program whatever is expedient to make
the test pass (turn green). You don't pain
yourself to come up with a duplication-free,
simple, clear design at this point. You'll drive
towards such a design later, when your test is
passing and you can comfortably experiment with
better designs. - RefactorYou improve the design of the code that
passed the test.
105TDD and continues refactoring
- Keep defect counts low
- Refactor without fear
- Produce simpler, better code
- Program without stress
106 Why should you refactor?
- To make easier put in new code.
- To improve the design of software.
- To make software easier to undersand.
- To make development more nice.
- It helps you to find a bugs.
- It helps you develop code more quickly.
107When should you refactor?
- The rule of three
- The first time you do something, you just do it.
The second time you do something similar, you
wince at the duplication, but you do the
duplicate thing anyway. The third time you do
something similar, you refactor.
108When should you refactor?
- Refactor when you add function.Refactor to help
you understand some code you need to
modify.Refactor to change design that does not
help you to add new feature easy. - Refactor when you need to fix a bug.
- Refactor as you do a code review.
- Design dept metphore and when shouldnt you
refactor.
109Refactoring and design
- You still do upfront design, but now you don't
try to find the solution. Instead all you want is
a reasonable solution. You know that as you build
the solution, as you understand more about the
problem, you realize that the best solution is
different from the one you originally came up
with. With refactoring this is not a problem, for
it no longer is expensive to make the changes.
110Refactoring and design
- Overdesign design that is more flexible than
you need, it make project bigger and more
complex. - Flexibility cost. With refactoring we move toward
simplicity of design rather than flexibility. - You dont know now which changes you will need
introduce to system in the future. Wrong
predicted direction of changes make some
flexibility newer used. - Emphasis on how difficult is it going to be to
refactor a simple solution into the flexible
solution? - Consciousness what you can refactor easy.
111So what is the strategy
- When you find you have to add a feature to a
program, and the program's code is not structured
in a convenient way to add the feature, first
refactor the program to make it easy to add the
feature, then add the feature. - Before you start refactoring, check that you have
a solid suite of tests. Thus the refactoring
should be self-checking. - Refactor the program in small steps. If you make
a mistake, it is easy to find the bug. - Dont optimize ahead, concentrate on simplicity
of design. Wait for result from diagnostics tools
for optimization.
112Bad smells in code
- Duplicated codeExtract method, Extract class,
Form Template Method, Introduce Polymorphic
Creation with Factory Method, Chain Constructors,
Replace One/Many Distinctions with Composite,
Extract Composite, Unify Interfaces with Adapter,
Introduce Null Object. - Long MethodExtract method, Introduce parameter
object, Compose Method, Move Accumulation to
Collecting Parameter, Replace Conditional
Dispatcher with Command, Move Accumulation to
Visitor,Replace Conditional Logic with Strategy
113Bad smells in code
- Conditional complexityReplace Conditional Logic
with Strategy, Move Embellishment to Decorator,
Replace State-Altering Conditionals with State,
Introduce Null Object. - Primitive obsessionReplace data value with
object, Replace type code with class, Replace
State-Altering Conditionals with State, Replace
Conditional Logic with Strategy, Replace Implicit
Tree with Composite, Replace Implicit Language
with Interpreter, Move Embellishment to
Decorator, Encapsulate Composite with Builder
114Bad smells in code
- Indecent ExposureEncapsulate Classes with
Factory - Solution SprawlMove Creation Knowledge to
Factory - Alternative classes with different
interfacesExtract superclass, Rename
method,Unify Interfaces with Adapter - Lazy classCollapse HierarchyInline Singleton
- Combinatorial explosionReplace Implicit Language
with Interpreter - Oddball solutionUnify Interfaces with Adapter
115Bad smells in code
- Large classExtract class, Extract subclass,
Extract interface,Replace Conditional Dispatcher
with Command, Replace State-Altering Conditionals
with State, Replace Implicit Language with
Interpreter - Switch statementsExtract method, Replace type
code with state/strategy, Replace conditional
with polimorphism,Replace Conditional Dispatcher
with Command, Move Accumulation to Visitor.
116Simple refactorings
- Support from development environment for
automation of refactoring VS2008 - Encapsulate field
- Rename
- Extract method
- Extract Interface
- Promote local variable to parameter
- Remove parameters
- Reorder parameters
117Simple refactorings
- Support from development environment for
automation of refactoring R - Make a method (non)static
- Push/pull members up/down
- Replace constructor with Factory Method
- Safe delete
- Move type/static method
118Self Encapsulate Field
- You are accessing a field directly, but the
coupling to the field is becoming awkward. - Create getting and setting methods for the field
and use only those to access the field. - It allows a subclass to override how to get that
information with a method and that it supports
more flexibility in managing the data, such as
lazy initialization, which initializes the value
only when you need to use it. - You are accessing a field in a superclass but you
want to override this variable access with a
computed value in the subclass. - Make code more complicated.
119Rename method
- The name of a method does not reveal its purpose.
- Change the name of the method.
120Extract method
- You have a code fragment that can be grouped
together. - Turn the fragment into a method whose name
explains the purpose of the method. - Pay attention to naming.
- Do it allways when sematic distance between
method body and method name is big. - It increases the chances that other methods can
use a method when the method is finely grained - It allows the higher-level methods to read more
like a series of comments. - Overriding also is easier when the methods are
finely grained.
121Extract method
- No Local Variables
- Using Local Variables (copy vs move)
- Reassigning a Local Variable
122Extract interface
- Several clients use the same subset of a class's
interface, or two classes have part of their
interfaces in common. - Extract the subset into an interface.
- Only a particular subset of a class's
responsibilities is used by a group of clients or
class needs to work with any class that can
handle certain requests. - Class has a distinct roles in different
situations. Make interface for each role. - You want to specify the outbound of the
operations the class makes on its server.
123Extract superclass
- You have two classes with similar features.
- Create a superclass and move the common features
to the superclass. - Prevents code duplication by class inherence.
124Extract subclass
- A class has features that are used only in some
instances. - Create a subclass for that subset of features.
- Class has behavior used for some instances of the
class and not for others. - Limitations
- You can't change the class-based behavior of an
object once the object is created. - You can change the class-based behavior with
Extract Class simply by plugging in different
components. - You can also use only subclasses to represent one
set of variations. - If you want the class to vary in several
different ways, you have to use delegation for
all but one of them.
125Pull up method
- You have methods with identical results on
subclasses. - Move them to the superclass.
- Eliminate code duplication.
- If method do the same but have different
signatures unify signatures. - If method are similiar but not the same consider
template method. - If two methods in different classes can be
parameterized in such a way that they end up as
essentially the same method then the smallest
step is to parameterize each method separately
and then generalize them. - If Pull Up Method body refer to features that are
on the subclass but not on the superclass
consider generalization that feature or creation
an abstract feature in the superclass.
126Move method
- A method is, or will be, using or used by more
features of another class than the class on which
it is defined. - Create a new method with a similar body in the
class it uses most. Either turn the old method
into a simple delegation, or remove it
altogether. - Do this if class has too much behaviour or when
classes are collaborating too much and are too
highly coupled.
127Move method - mechanics
- Examine all features used by the source method
that are defined on the source class. Consider
whether they also should be moved. - Check the sub- and superclasses of the source
class for other declarations of the method. - Declare the method in the target class.
- Copy the code from the source method to the
target. Adjust the method to make it work in - its new home.
- Compile the target class.
- Determine how to reference the correct target
object from the source. - Turn the source method into a delegating method.
- Compile and test.
- Decide whether to remove the source method or
retain it as a delegating method. - If you remove the source method, replace all the
references with references to the target method. - Compile and test.
- R make method static/move/make method unstatic
128Compose method
- You can't rapidly understand a method's logic.
- Transform the logic into a small number of
intention-revealing steps at the same level of
detail. - Efficiently communicates what a method does and
how it does what it does. - Simplifies a method by breaking it up into
well-named chunks of behavior at the same level
of detail. - Can lead to an overabundance of small methods.
- Can make debugging difficult because logic is
spread out across many small methods.
129Compose method - mechanics
- Think small.
- Remove duplication and dead code.
- Communicate intention.
- Simplify.
- Use the same level of detail.
130Replace Constructors with Creation Methods
- There is a number of different, not trivial
constructors - Creation Methods have meaningful names
- Creation Methods can have identical parameters
- Mechanics
- Create Gathering Constructor (Chain Constructors)
- Compile and Run
- Find a constructor Call
- Create a proprietary static method
- Replace constructor calls with a method
- Make the constructor private
131Replace inherience with delegation
- A subclass uses only part of a superclasses
interface or does not want to inherit data. - Create a field for the superclass, adjust methods
to delegate to the superclass, and remove the
subclassing. - By using delegation instead, you make it clear
that you are making only partial use of the
delegated class. You control which aspects of the
interface to take and which to ignore. The cost
is extra delegating methods that are boring to
write but are too simple to go wrong. - You need to use functionality or implementation?
- Changing behaviour in runtime by changing
reference to object which we use to delegate
functionality.
132Replace inherience with delegation mechanics
- Create a field in the subclass that refers to an
instance of the superclass. Initialize it to
this. - Change each method defined in the subclass to use
the delegate field. Compile and test after
changing each method. - Remove the subclass declaration and replace the
delegate assignment with an assignment to a new
object. - For each superclass method used by a client, add
a simple delegating method. - Compile and test.
133Replace conditional with polymorphism
- You have a conditional that chooses different
behavior depending on the type of an object. - Move each leg of the conditional to an overriding
method in a subclass. Make the original method
abstract. - The biggest gain occurs when this same set of
conditions appears in many places in the program.
- Add a new case just by creation a new subclass
and provide the appropriate methods. - Clients of the class don't need to know about the
subclasses, which reduces the dependencies.
134Replace type code with class
- A class has a simple type code (int, String)
that does not affect its behavior. - Replace the number with a new class.
- A field's type fails to protect it from unsafe
assignments and invalid equality comparisons. - Constrain the assignments and equality
comparisons and provides better protection from
invalid operations. - Compilation time detection for invalid
operations. - If you replace the number with a class, the
compiler can type check on the class. By
providing factory methods for the class, you can
statically check that only valid instances are
created and that those instances are passed on to
the correct objects. - If type code is not pure data but it affect state
consider Replace type code with subclasses or
Replace type code with State/Strategy. - Requires more code than using unsafe type does.
135Replace type code with subclasses
- You have an immutable type code that affects the
behavior of a class. - Replace the type code with subclasses by creation
a subclass for each type code. - To deal with presence of case-like conditional
statements which test the value of the type code
and then execute different code depending on the
value of the type code or presence of features
that are relevant only to objects with certain
type codes. - Cooperates with Replace Conditional with
Polimorphism, Push Down Method. - Problems when type code changes after the object
is created or class with the type code is already
subclassed for another reason. In that case
consider Replace Type Code with State/Strategy. - It moves knowledge of the variant behavior from
clients of the class to the class itself. Adding
new variants is simply adding a subclass. Without
polymorphism You have to find all the
conditionals and change those. So this
refactoring is particularly valuable when
variants keep changing.
136Replace type code with subclassesmechanics
- Create a new class for the type code.
- Modify the implementation of the source class to
use the new class. - Compile and test.
- For each method on the source class that uses the
code, create a new method that uses the new class
instead. - One by one, change the clients of the source
class so that they use the new interface. - Compile and test after each client is updated.
- Remove the old interface that uses the codes, and
remove the static declarations of the codes. - Compile and test.
137Replace type code with subclasses mechanics
- Self-encapsulate the type code.
- For each value of the type code, create a
subclass. Override the getting method of the type
code in the subclass to return the relevant
value. - Compile and test after replacing each type code
value with a subclass. - Remove the type code field from the superclass.
Declare the accessors for the type code as
abstract. - Compile and test.
138Replace Type Code with State/Strategy
- You have a type code that affects the behavior of
a class, but you cannot use subclassing. - Replace the type code with a state object.
- Self-encapsulate the type code.
- Create a new class, and name it after the purpose
of the type code. This is the state object. - Add subclasses of the state object, one for each
type code. - Create an abstract query in the state object to
return the type code. Create overriding queries
of each state object subclass to return the
correct type code. - Compile.
- Create a field in the old class for the new state
object. - Adjust the type code query on the original class
to delegate to the state object. - Adjust the type code setting methods on the
original class to assign an instance of the
appropriate state objec