Integration testing vs Unit testing Using Mocks and stubs Reading: The Art of Unit Testing, Ch. 1, 3, 4-5 (Osherove) Code Complete, Ch. 29 (McConnell) slides based on material by Marty Stepp http://www.cs.washington.edu/403/
Agenda • LogAnalyzer: a “test” case • Unit vs Integration testing • Design Principles for Testable Code • Stubs vs Mocks • Demo LogAnalyzer in Java
LogAnalyzer
Unit Tests vs. Other Kinds of Tests • Performed on the class (“unit”) level • Each unit is tested in isolation • Automatic • Create class object, call methods, check results
Unit Testing Context
Class Under Test May Call Many Others ClassUnderTest ClassA Dependency3Dependency1 Dependency2 ClassB ClassC ClassD ClassE F G H I J K L M N Database Network File System Printer Nuclear Rocket Launcher
Design Principles for Testable Code
Beware hard coded dependencies double GetPrice(int productId) { using (SqlConnection conn = new SqlConnection( Config.ProductsDbConnectionString)) { conn.Open(); double price = ReadPriceFromDb(conn, productId); if (DateTime.Now.DayOfWeek==DayOfWeek.Wednesday) { // apply Wednesday discount price *= 0.95; } return price; } }
Replaceable dependencies • All dependencies are explicitly passed in constructor/method parameters or properties • If it is not passed, it should not be used • Must avoid hidden dependencies • Static methods (like DateTime.Now) • Singletons • “new” • The only place where things are “new’ed” is a factory class or a factory method, which is not unit tested
Dependency Inversion vs Dependency Injection • A principle, one of the SOLID principles • High-level modules should not depend upon low-level modules. Both should depend upon abstractions. • Abstractions should not depend upon details. Details should depend upon abstractions. • A technique offered by frameworks that use an IoC-container but can also be applied manually. Push mechanism (Tell, Don’t Ask or the Hollywood Principe: Don’t Call us, We call You), you can also use a pull mechanism (Ask, Don’t Tell), e.g. a ServiceLocator or Plug-In.
LogAnalyzer: Where do we put the interfaces?
Not compliant to DIP
Compliant with DIP. Any other options?
Multiple client depend on a component’s interface
Unit vs Integration Tests
Unit vs Integration Tests • Tests that involve many classes are integration tests • In other words: test reading from a database is not a unit test • Integration tests verify the wiring between classes • Unit tests verify classes in isolation • We need both: neither is a replacement of the other
Integration integration: Combining 2 or more software units – often a subset of the overall project (!= system testing) Why do software engineers care about integration? – new problems will inevitably surface • many systems now together that have never been before – if done poorly, all problems present themselves at once • hard to diagnose, debug, fix – cascade of interdependencies • cannot find and solve problems one-at-a-time
Phased integration phased ("big-bang") integration: – design, code, test, debug each class/unit/subsystem separately – combine them all – pray
Incremental integration incremental integration: – develop a functional "skeleton" system – design, code, test, debug a small new piece – integrate this piece with the skeleton • test/debug it before adding any other pieces
Benefits of incremental Benefits: – Errors easier to isolate, find, fix • reduces developer bug-fixing load – System is always in a (relatively) working state • good for customer relations, developer morale Drawbacks: – May need to create "stub" versions of some features that have not yet been integrated
Top-down integration top-down integration: Start with outer UI layers and work inward – must write (lots of) stub lower layers for UI to interact with – allows postponing tough design/debugging decisions (bad?)
Bottom-up integration bottom-up integration: Start with low-level data/logic layers and work outward – must write test drivers to run these layers – won't discover high-level / UI design flaws until late
Daily builds daily build: Compile working executable on a daily basis – allows you to test the quality of your integration so far – helps morale; product "works every day"; visible progress – best done automated or through an easy script – quickly catches/exposes any bug that breaks the build smoke test: A quick set of tests run on the daily build. – NOT exhaustive; just sees whether code "smokes" (breaks) – used (along with compilation) to make sure daily build runs continuous integration: Adding new units immediately as they are written.
Integration testing integration testing: Verifying software quality by testing two or more dependent software modules as a group. challenges: – Combined units can fail in more places and in more complicated ways. – How to test a partial system where not all parts exist? – How to "rig" the behavior of unit A so as to produce a given behavior from unit B?
Test Doubles
Test Doubles (Gerard, Fowler) https://martinfowler.com/bliki/TestDouble.html • Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists. • Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an InMemoryTestDatabase is a good example). • Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. • Spies are stubs that also record some information based on how they were called. One form of this might be an email service that records how many messages it was sent. • Mocks are pre-programmed with expectations which form a specification of the calls they are expected to receive. They can throw an exception if they receive a call they don't expect and are checked during verification to ensure they got all the calls they were expecting.
Stubs stub: A controllable replacement for an existing software unit to which your code under test has a dependency. – useful for simulating difficult-to-control elements: • network / internet • database • time/date-sensitive code • files • threads • memory – also useful when dealing with brittle legacy code/systems
Create a stub, step 1 Identify the external dependency. – This is either a resource or a class/object. – If it isn't an object, wrap it up into one. • (Suppose that Class A depends on troublesome Class B.)
Create a stub, step 2 Extract the core functionality of the object into an interface. – Create an InterfaceB based on B – Change all of A's code to work with type InterfaceB, not B
Create a stub, step 3 Write a second "stub" class that also implements the interface, but returns pre-determined fake data. – Now A's dependency on B is dodged and can be tested easily. – Can focus on how well A integrates with B's external behavior.
Injecting a stub seams: Places to inject the stub so Class A will talk to it. – at construction (not ideal) A aardvark = new A(new StubB()); – through a getter/setter method (better) A apple = new A(...); aardvark.setResource(new StubB()); – just before usage, as a parameter (also better) aardvark.methodThatUsesB(new StubB()); • You should not have to change A's code everywhere (beyond using your interface) in order to use your Stub B. (a "testable design")
"Mock" objects mock object: A fake object that decides whether a unit test has passed or failed by watching interactions between objects. – useful for interaction testing (as opposed to state testing)
Stubs vs. mocks – A stub gives out data that goes to the object/class under test. – The unit test directly asserts against class under test, to make sure it gives the right result when fed this data. – A mock waits to be called by the class under test (A). • Maybe it has several methods it expects that A should call. – It makes sure that it was contacted in exactly the right way. • If A interacts with B the way it should, the test passes.
Mock object frameworks Stubs are often best created by hand/IDE. Mocks are tedious to create manually. Mock object frameworks help with the process. – android-mock, EasyMock, jMock, Mockito (Java) – FlexMock / Mocha (Ruby) – SimpleTest / PHPUnit (PHP) – ... Frameworks provide the following: – auto-generation of mock objects that implement a given interface – logging of what calls are performed on the mock objects – methods/primitives for declaring and asserting your expectations
Using stubs/mocks together Suppose a log analyzer reads from a web service. If the web fails to log an error, the analyzer must send email. – How to test to ensure that this behavior is occurring? Set up a stub for the web service that intentionally fails. Set up a mock for the email service that checks to see whether the analyzer contacts it to send an email message.
Initial Design - Sequence Diagram
Initial Design - Package/Class Diagram

Integration and Unit Testing in Java using Test Doubles like mocks and stubs

  • 1.
    Integration testing vsUnit testing Using Mocks and stubs Reading: The Art of Unit Testing, Ch. 1, 3, 4-5 (Osherove) Code Complete, Ch. 29 (McConnell) slides based on material by Marty Stepp http://www.cs.washington.edu/403/
  • 2.
    Agenda • LogAnalyzer: a“test” case • Unit vs Integration testing • Design Principles for Testable Code • Stubs vs Mocks • Demo LogAnalyzer in Java
  • 3.
  • 4.
    Unit Tests vs.Other Kinds of Tests • Performed on the class (“unit”) level • Each unit is tested in isolation • Automatic • Create class object, call methods, check results
  • 5.
  • 6.
    Class Under TestMay Call Many Others ClassUnderTest ClassA Dependency3Dependency1 Dependency2 ClassB ClassC ClassD ClassE F G H I J K L M N Database Network File System Printer Nuclear Rocket Launcher
  • 7.
    Design Principles forTestable Code
  • 8.
    Beware hard codeddependencies double GetPrice(int productId) { using (SqlConnection conn = new SqlConnection( Config.ProductsDbConnectionString)) { conn.Open(); double price = ReadPriceFromDb(conn, productId); if (DateTime.Now.DayOfWeek==DayOfWeek.Wednesday) { // apply Wednesday discount price *= 0.95; } return price; } }
  • 9.
    Replaceable dependencies • Alldependencies are explicitly passed in constructor/method parameters or properties • If it is not passed, it should not be used • Must avoid hidden dependencies • Static methods (like DateTime.Now) • Singletons • “new” • The only place where things are “new’ed” is a factory class or a factory method, which is not unit tested
  • 11.
    Dependency Inversion vsDependency Injection • A principle, one of the SOLID principles • High-level modules should not depend upon low-level modules. Both should depend upon abstractions. • Abstractions should not depend upon details. Details should depend upon abstractions. • A technique offered by frameworks that use an IoC-container but can also be applied manually. Push mechanism (Tell, Don’t Ask or the Hollywood Principe: Don’t Call us, We call You), you can also use a pull mechanism (Ask, Don’t Tell), e.g. a ServiceLocator or Plug-In.
  • 12.
    LogAnalyzer: Where dowe put the interfaces?
  • 13.
  • 14.
    Compliant with DIP.Any other options?
  • 15.
    Multiple client dependon a component’s interface
  • 16.
  • 17.
    Unit vs IntegrationTests • Tests that involve many classes are integration tests • In other words: test reading from a database is not a unit test • Integration tests verify the wiring between classes • Unit tests verify classes in isolation • We need both: neither is a replacement of the other
  • 18.
    Integration integration: Combining 2or more software units – often a subset of the overall project (!= system testing) Why do software engineers care about integration? – new problems will inevitably surface • many systems now together that have never been before – if done poorly, all problems present themselves at once • hard to diagnose, debug, fix – cascade of interdependencies • cannot find and solve problems one-at-a-time
  • 19.
    Phased integration phased ("big-bang")integration: – design, code, test, debug each class/unit/subsystem separately – combine them all – pray
  • 20.
    Incremental integration incremental integration: –develop a functional "skeleton" system – design, code, test, debug a small new piece – integrate this piece with the skeleton • test/debug it before adding any other pieces
  • 21.
    Benefits of incremental Benefits: –Errors easier to isolate, find, fix • reduces developer bug-fixing load – System is always in a (relatively) working state • good for customer relations, developer morale Drawbacks: – May need to create "stub" versions of some features that have not yet been integrated
  • 22.
    Top-down integration top-down integration: Startwith outer UI layers and work inward – must write (lots of) stub lower layers for UI to interact with – allows postponing tough design/debugging decisions (bad?)
  • 23.
    Bottom-up integration bottom-up integration: Startwith low-level data/logic layers and work outward – must write test drivers to run these layers – won't discover high-level / UI design flaws until late
  • 24.
    Daily builds daily build:Compile working executable on a daily basis – allows you to test the quality of your integration so far – helps morale; product "works every day"; visible progress – best done automated or through an easy script – quickly catches/exposes any bug that breaks the build smoke test: A quick set of tests run on the daily build. – NOT exhaustive; just sees whether code "smokes" (breaks) – used (along with compilation) to make sure daily build runs continuous integration: Adding new units immediately as they are written.
  • 25.
    Integration testing integration testing:Verifying software quality by testing two or more dependent software modules as a group. challenges: – Combined units can fail in more places and in more complicated ways. – How to test a partial system where not all parts exist? – How to "rig" the behavior of unit A so as to produce a given behavior from unit B?
  • 26.
  • 27.
    Test Doubles (Gerard,Fowler) https://martinfowler.com/bliki/TestDouble.html • Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists. • Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an InMemoryTestDatabase is a good example). • Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. • Spies are stubs that also record some information based on how they were called. One form of this might be an email service that records how many messages it was sent. • Mocks are pre-programmed with expectations which form a specification of the calls they are expected to receive. They can throw an exception if they receive a call they don't expect and are checked during verification to ensure they got all the calls they were expecting.
  • 28.
    Stubs stub: A controllablereplacement for an existing software unit to which your code under test has a dependency. – useful for simulating difficult-to-control elements: • network / internet • database • time/date-sensitive code • files • threads • memory – also useful when dealing with brittle legacy code/systems
  • 29.
    Create a stub,step 1 Identify the external dependency. – This is either a resource or a class/object. – If it isn't an object, wrap it up into one. • (Suppose that Class A depends on troublesome Class B.)
  • 30.
    Create a stub,step 2 Extract the core functionality of the object into an interface. – Create an InterfaceB based on B – Change all of A's code to work with type InterfaceB, not B
  • 31.
    Create a stub,step 3 Write a second "stub" class that also implements the interface, but returns pre-determined fake data. – Now A's dependency on B is dodged and can be tested easily. – Can focus on how well A integrates with B's external behavior.
  • 32.
    Injecting a stub seams:Places to inject the stub so Class A will talk to it. – at construction (not ideal) A aardvark = new A(new StubB()); – through a getter/setter method (better) A apple = new A(...); aardvark.setResource(new StubB()); – just before usage, as a parameter (also better) aardvark.methodThatUsesB(new StubB()); • You should not have to change A's code everywhere (beyond using your interface) in order to use your Stub B. (a "testable design")
  • 33.
    "Mock" objects mock object:A fake object that decides whether a unit test has passed or failed by watching interactions between objects. – useful for interaction testing (as opposed to state testing)
  • 34.
    Stubs vs. mocks –A stub gives out data that goes to the object/class under test. – The unit test directly asserts against class under test, to make sure it gives the right result when fed this data. – A mock waits to be called by the class under test (A). • Maybe it has several methods it expects that A should call. – It makes sure that it was contacted in exactly the right way. • If A interacts with B the way it should, the test passes.
  • 35.
    Mock object frameworks Stubsare often best created by hand/IDE. Mocks are tedious to create manually. Mock object frameworks help with the process. – android-mock, EasyMock, jMock, Mockito (Java) – FlexMock / Mocha (Ruby) – SimpleTest / PHPUnit (PHP) – ... Frameworks provide the following: – auto-generation of mock objects that implement a given interface – logging of what calls are performed on the mock objects – methods/primitives for declaring and asserting your expectations
  • 36.
    Using stubs/mocks together Supposea log analyzer reads from a web service. If the web fails to log an error, the analyzer must send email. – How to test to ensure that this behavior is occurring? Set up a stub for the web service that intentionally fails. Set up a mock for the email service that checks to see whether the analyzer contacts it to send an email message.
  • 37.
    Initial Design -Sequence Diagram
  • 38.
    Initial Design -Package/Class Diagram

Editor's Notes

  • #35 Don't put assertion statements directly in the mock object, because we may want to re-use it.