Download presentation
Presentation is loading. Please wait.
Published byRosaline Bryan Modified over 9 years ago
1
Using UML, Patterns, and Java Object-Oriented Software Engineering Chapter 11, Testing: Testing Patterns Note to Instructor: The material in this slide set is not contained in the 3 rd edition of the text book. It is planned for the 4 th edition.
2
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 2 Recall: The System Model for the Simple Inventory System
3
Java Code for the Simple Inventory System public class InventorySystem { private static String TALISKER = "Talisker"; private int totalStock; private Warehouse warehouse = new WarehouseImpl(); public void addToWarehouse(String item, int amount) { warehouse.add(item, amount); totalStock += amount; } public int getTotalStock() { return totalStock; } public boolean processOrder(Order order) { order.fill(warehouse); return order.isFilled(); } public static void main(String[] args) { InventorySystem inventorySystem = new InventorySystem(); inventorySystem.addToWarehouse(TALISKER, 50); boolean order1success = inventorySystem.processOrder(new OrderImpl(TALISKER, 50)); boolean order2success = inventorySystem.processOrder(new OrderImpl(TALISKER, 51)); System.out.println("Order1 succeeded? " + order1success + " - Order2 succeeded? " + order2success); } ? Note in this example the UML Client method useInventorySystem() is realized as the main() method in the class InventorySystem
4
Java Code for the Warehouse public interface Warehouse { public boolean hasInventory(String item, int amount); public int getInventory(String item); public void add(String item, int amount); public void remove(String item, int amount); } public class WarehouseImpl implements Warehouse { private Map inventory = new HashMap (); public boolean hasInventory(String item, int amount) { return inventory.get(item) >= amount; } public int getInventory(String item) { return inventory.get(item); } public void add(String item, int amount) { inventory.put(item, amount); } public void remove(String item, int amount) { inventory.put(item, inventory.get(item) - amount); }
5
Java Code for the Order public interface Order { public boolean isFilled(); public void fill(Warehouse warehouse); } public class OrderImpl implements Order { private String item; private int amount; private boolean filled = false; public OrderImpl(String item, int amount) { this.item = item; this.amount = amount; } public boolean isFilled() { return filled; } public void fill(Warehouse warehouse) { if(warehouse.hasInventory(item, amount)) { warehouse.remove(item, amount); filled = true; }
6
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 6 Test Model of the InventorySystem Assume we now want to unit test the class Order, our SUT Note: Order is not unit testable in isolation, it depends on Warehouse
7
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 7 Unit testing Order with jUnit public class OrderStateTester { private static String TALISKER = "Talisker"; private Warehouse warehouse; @Before public void setUp() { warehouse = new WarehouseImpl(); warehouse.add(TALISKER, 50); } @Test public void orderIsFilledIfEnoughInWarehouse() { Order order = new Order(TALISKER, 50); order.fill(warehouse); assertTrue(order.isFilled()); assertEquals(0, warehouse.getInventory(TALISKER)); } @Test public void orderDoesNotRemoveIfNotEnough() { Order order = new Order(TALISKER, 51); order.fill(warehouse); assertFalse(order.isFilled()); assertEquals(50, warehouse.getInventory(TALISKER)); } + Needed for unit testing Order: Instantiation of WarehouseImpl
8
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 8 From State Testing to Behavior Testing Observation: jUnit helps us to test the state of a SUT What if we not only want to test the state of a SUT, but also its interaction with its collaborators, for example the interaction between Order and Warehouse? Limitation of JUnit: jUnit does not provide mechanisms to test a specific sequence of operations, that is, which operations are called on collaborators and in which order Here Mock Objects come into play.
9
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 9 Outline of the Lecture Unit testing with jUnit (Example) Mock object pattern (Example) Dependency injection pattern (not only a testing pattern) 4 stage testing pattern Inner class test pattern Reflection test pattern Test exception pattern
10
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 10 Recall: Subclasses of Test Doubles There are 4 types of test doubles. All doubles try to make the SUT believe it is talking to its real collaborators: Dummy object: Passed around but never actually used. Dummy objects are usually used to fill parameter lists Fake object: A fake object is a working implementation, but usually contains some type of “shortcut” which makes it not suitable for production code (Example: A database stored in memory instead of a real database) Stub: Provides canned answers to calls made during the test, but it is not able to respond to anything outside what it is programmed for Mock object: Mocks are able to mimic the behavior of the real object. They know how to deal with sequence of calls they are expected to receive.
11
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 11 Mock-Object Pattern In the mock object pattern a mock object replaces the behavior of a real object called the collaborator and returns hard-coded values A mock object can be created at startup- time with a factory pattern Mock objects can be used for testing state of individual objects as well as the interaction between objects, that is, to validate that the interactions of the SUT with its collaborators behave is as expected The selection of the Collaborator or Mock Collaborator is called binding (cf. Lecture 2) Mock object
12
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 12 Implementing Mock Objects Create an instance of the mock object and bind it to the abstract class or interface Set the state in the mock object Set any parameters or attributes that could be used by the object to test Set expectations in the mock object Here the desired or expected outcome is specified. This includes the number of method calls and the return values of mock object invocations Invoke SUT code with mock object(s) as parameter(s) Now that all of the expectations have been set, use the mock object in the SUT Validate consistency in the Mock Object Implement a “verify” method, which should be the final step in the test to validate that the expected outcome matches the observed outcome Note we prefer the term validation instead of verification. Source: Brown & Tapolcsanyi: Mock Object Patterns. In Proceedings of the 10th Conference on Pattern Languages of Programs, 2003. http://hillside.net/plop/plop2003/papers.htmlhttp://hillside.net/plop/plop2003/papers.html
13
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 13 Frameworks that implement Mock Objects jMock EasyMock MockPP GoogleMock Test::MockObject RSpec See a more comprehensive list of implementations at http://www.mockobjects.com/ http://www.mockobjects.com/ In the following examples, we use EasyMock. Java C++ Perl Ruby
14
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 14 The EasyMock Framework The goal of the EasyMock framework is to make it easy to deal with mock objects EasyMock operates using static method imports (org.easymock.EasyMock.*) It uses a record/replay metaphor Necessary steps in using the EasyMock framework: 1.Create the mock object for the interface of the collaborator (createMock(Class interface)) 2.Set its state and/or record expected behavior (expect(T methodCall).andReturn(T returnValue)) 3.Switch mock object to replay state (“ready to play”) (replay(mock)) and invoke SUT code 4.Validate behavior (verify(mock)) For more information on EasyMock see: http://easymock.org/http://easymock.org/
15
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 15 Test Model of the InventorySystem with a Mock Object warehouseMock supplied by EasyMock
16
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 16 Testcase using the Mock Object Pattern Unit test fillingRemovesInventoryIfInStock: 1.Check that the warehouse has bottles in its inventory 2.Withdraw the 50 bottles 3.Make sure the order is filled Flow of events specifying the expected behavior:
17
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 17 Unit Test: fillingRemovesInventoryIfInStock public class OrderInteractionTesterEasyMock { private static String TALISKER = "Talisker"; private Warehouse warehouseMock; @Test public void fillingRemovesInventoryIfInStock() { Order order = new OrderImpl(TALISKER, 50); warehouseMock = createMock(Warehouse.class); expect(warehouseMock.hasInventory(TALISKER, 50)).andReturn(true); warehouseMock.remove(TALISKER, 50); replay(warehouseMock); order.fill(warehouseMock); assertTrue(order.isFilled()); verify(warehouseMock); } 1. Create mock object 2. Specify expected behavior (“behavioral oracle”) 3. Set mock object to be ready to play 4. Validate observed behavior against expected behavior
18
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 18 Mock Objects Summary Mock objects are test doubles that are able to mimic the behavior of the real object The mock object pattern enables us to test state and behavior Mock objects are a way to create “self-contained” unit tests (i.e. without much interaction with the rest of the objects in the system model) Frameworks for mock objects are available in major programming languages (Java, C++, Perl, Ruby,…).
19
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 19 Where are we now? Unit testing with jUnit (Example) Mock object pattern (Example) Dependency injection pattern (not only a testing pattern) 4 stage testing pattern Inner class test pattern Reflection test pattern Test exception pattern
20
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 20 Recall the InventorySystem public class InventorySystem { private static String TALISKER = "Talisker“; private int totalStock; private Warehouse warehouse = new WarehouseImpl(); … } Since the InventorySystem explicitly calls new to instantiate a specific implementation of Warehouse, we cannot test this InventorySystem implementation with a mock Warehouse Can the InventorySystem implementation be changed to work with both, a production implementation of Warehouse and a mock Warehouse? Problem: Unit testing the InventorySystem ➞ Dependency Injection
21
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 21 Instantiation with new What is wrong with the usual way of instantiating objects with new ?
22
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 22 Instantiation with new What is wrong with the usual way of instantiating objects with new ? new creates a direct compile time dependency on the implementation !
23
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 23 Instantiation with new What is wrong with the usual way of instantiating objects with new ? new creates a direct compile time dependency on the implementation Also, we cannot switch to a mock object without changing the Client code (our SUT) Ok, so new is bad. What about using a Factory? ?
24
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 24 Instantiation with Factories This removes the dependency on the implementation But now there is a new association to ServiceFactory, which provides access to the subclasses ServiceImpl and MockServiceImpl ServiceFactory depends on these implementations, so as a result Client still has compile-time dependencies on implementation classes Additionally we need to write a lot of Factory boilerplate code Can we get rid off this new association? Yes, with dependency injection. ! ? ??
25
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 25 Instantiation with Dependency Injection The basic idea behind dependency injection is to remove instantiations from the Client’s responsibility entirely and to let the implementation be supplied by an outside source via… … a constructor: public class Client { private Service service; public Client(Service service) { this.service=service; } … }
26
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 26 Instantiation with Dependency Injection The basic idea behind dependency injection is to remove instantiations from the Client’s responsibility entirely and to let the implementation be supplied by an outside source via... … a constructor: public class Client { private Service service; public Client(Service service) { this.service=service; } … } … a setter: public class Client { private Service service; public void setService(Service service) { this.service=service; } … }
27
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 27 Instantiation with Dependency Injection Adding a constructor/setter gets rid off the previous associations but the problem of instantiation still is not solved – who creates the concrete instance and injects it? In order to really decouple object creation, we need a framework supplying the implementation subclasses Allows configuring binding of implementation classes to interfaces Still provides the advantages of the Factory Pattern. ?
28
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 28 Introduction to Dependency Injection This is what we have with factories: 3 additional associations that increase the coupling between objects High coupling is bad!
29
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 29 What we have and what we want
30
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 30 Introduction to Dependency Injection This is what we want: Client only depends on the interface Service Low coupling!
31
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 31 Introduction to Dependency Injection The dependency injection framework reduces the coupling: The Client now only knows the Service interface. There are no compile-time dependencies to implementations anymore The implementations are injected by the framework Binding (the selection of the “Impl” subclass to be instantiated for a certain interface) is also done by the framework Because there is no factory, the Client class can be more easily reused.
32
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 32 Dependency Injection Frameworks There are several frameworks for dependency injection For a list of frameworks (also in other programming languages) see http://en.wikipedia.org/wiki/Dependency_injection#Existing_frameworks http://en.wikipedia.org/wiki/Dependency_injection#Existing_frameworks Some examples for Java are: J2EE 5 Includes dependency injection as an alternative to JNDI Spring Uses XML to configure the binding of the specific subclass implementation to the interface Google Guice Uses Java 5 with annotations for configuration these bindings In the following we use Google Guice and explain its usage by code examples.
33
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 33 Example: Existing Inventorysystem public class InventorySystem { private static String TALISKER = "Talisker"; private int totalStock; private Warehouse warehouse = new WarehouseImpl(); public void addToWarehouse(String item, int amount) { warehouse.add(item, amount); totalStock += amount; } public int getTotalStock() { return totalStock; } public boolean processOrder(Order order) { order.fill(warehouse); return order.isFilled(); } public static void main(String[] args) { InventorySystem inventorySystem = new InventorySystem(); inventorySystem.addToWarehouse(TALISKER, 50); boolean order1success = inventorySystem.processOrder(new OrderImpl(TALISKER, 50)); boolean order2success = inventorySystem.processOrder(new OrderImpl(TALISKER, 51)); System.out.println("Order1 succeeded? " + order1success + " - Order2 succeeded? " + order2success); }
34
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 34 Example: InventorySystem with Guice public class InventorySystemDI { private static String TALISKER = "Talisker"; private int totalStock; @Inject private Warehouse warehouse; public void addToWarehouse(String item, int amount) { warehouse.add(item, amount); totalStock += amount; } public int getTotalStock() { return totalStock; } public boolean processOrder(Order order) { order.fill(warehouse); return order.isFilled(); } public static void main(String[] args) { Injector injector = Guice.createInjector(new ProductionModule()); InventorySystemDI inventorySystem = injector.getInstance(InventorySystemDI.class); inventorySystem.addToWarehouse(TALISKER, 50); boolean order1success = inventorySystem.processOrder(new OrderImpl(TALISKER, 50)); boolean order2success = inventorySystem.processOrder(new OrderImpl(TALISKER, 51)); System.out.println("Order1 succeeded? " + order1success + " - Order2 succeeded? " + order2success); } Can you spot the difference to the old code?
35
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 35 Can you spot the difference to the old one? public class InventorySystemDI { private static String TALISKER = "Talisker"; private int totalStock; @Inject private Warehouse warehouse; public void addToWarehouse(String item, int amount) { warehouse.add(item, amount); totalStock += amount; } public int getTotalStock() { return totalStock; } public boolean processOrder(Order order) { order.fill(warehouse); return order.isFilled(); } public static void main(String[] args) { Injector injector = Guice.createInjector(new ProductionModule()); InventorySystemDI inventorySystem = injector.getInstance(InventorySystemDI.class); inventorySystem.addToWarehouse(TALISKER, 50); boolean order1success = inventorySystem.processOrder(new OrderImpl(TALISKER, 50)); boolean order2success = inventorySystem.processOrder(new OrderImpl(TALISKER, 51)); System.out.println("Order1 succeeded? " + order1success + " - Order2 succeeded? " + order2success); } These are the changes to the “Client” Notice there is no “new” here anymore! Here an injector is created that allows to specify bindings of implementations to interfaces. Uses a ProductionModule. See slide 41 What is an injector?
36
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 36 What is an Injector? How do you get the Jam into the Krapfen? InventorySystemInjector filled with “Jam” (i.e. Warehouse)
37
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 37 Guice uses the concept of a Module to bind a subclass implementation to an interface Here is the Guice Module ProductionModule that binds WarehouseImpl to Warehouse : Guice Modules: Configure the Bindings public class ProductionModule implements Module { public void configure(Binder binder) { binder.bind(Warehouse.class).to(WarehouseImpl.class); } }
38
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 38 1.Tell Guice what to inject using the @Inject annotation Constructor injection Method injection (for setter methods) Field injection 2.Define Binding 3. Instantiate an Injector and tell it which Module to use (i.e. provide a list of available bindings) 4. Create instance classes needing injection by requesting an instance from the Injector (in our case: InventorySystem) For more details: http://code.google.com/p/google-guice/wiki/UserGuide http://code.google.com/p/google-guice/wiki/UserGuide 4 Steps to use Guice
39
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 39 InventorySystem with Guice Code public class InventorySystemDI { private static String TALISKER = "Talisker"; private int totalStock; @Inject private Warehouse warehouse; public void addToWarehouse(String item, int amount) { warehouse.add(item, amount); totalStock += amount; } public int getTotalStock() { return totalStock; } public boolean processOrder(Order order) { order.fill(warehouse); return order.isFilled(); } public static void main(String[] args) { Injector injector = Guice.createInjector(new ProductionModule()); InventorySystemDI inventorySystem = injector.getInstance(InventorySystemDI.class); inventorySystem.addToWarehouse(TALISKER, 50); boolean order1success = inventorySystem.processOrder(new OrderImpl(TALISKER, 50)); boolean order2success = inventorySystem.processOrder(new OrderImpl(TALISKER, 51)); System.out.println("Order1 succeeded? " + order1success + " - Order2 succeeded? " + order2success); }
40
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 40 Guice and Unit Testing How does this help us with unit testing? Remember – instead of this hard-coded dependency: public class InventorySystem { private Warehouse warehouse = new WarehouseImpl(); … } we now have: public class InventorySystem { @Inject private Warehouse warehouse; … } As a result we are now able to write a unit test that binds warehouse to a mock To do this we define another Guice Module called TestModule. InventorySystem Injector filled with Warehouse
41
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 41 The Guice module for the production code: Configuring the Bindings public class ProductionModule implements Module { public void configure(Binder binder) { binder.bind(Warehouse.class).to(WarehouseImpl.class); } public class TestModule implements Module { public void configure(Binder binder) { Warehouse warehouseMock = EasyMock.createMock(Warehouse.class); binder.bind(Warehouse.class).toInstance(warehouseMock); } The Guice module for the unit test:
42
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 42 Unit Test of the InventorySystem using Guice and EasyMock public class InventorySystemDITest { private static String TALISKER = "Talisker"; private InventorySystemDI inventorySystem; private Injector injector; @Before public void setUp() throws Exception { injector = Guice.createInjector(new TestModule()); inventorySystem = injector.getInstance(InventorySystemDI.class); } @Test public void addToWarehouse() { Warehouse warehouseMock = injector.getInstance(Warehouse.class); warehouseMock.add(TALISKER, 50); EasyMock.replay(warehouseMock); inventorySystem.addToWarehouse(TALISKER, 50); assertEquals(inventorySystem.getTotalStock(), 50); EasyMock.verify(warehouseMock); } Here we retrieve the mock Warehouse from the injector to use it during the test. Here we tell Guice to create an injector using TestModule, that binds Warehouse to a Warehouse Mock We let Guice instantiate the InventorySystem and the Warehouse in it
43
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 43 What have we learned? Simple unit tests allow to test state Use the Mock Object Pattern to unit test behavior Enforce low coupling with the Dependency Injection Pattern Are there other patterns applicable to testing? Yes, there are! Meszaros describes 68 testing patterns Gerard Meszaros: xUnit Test Patterns – Refactoring Test Code. Martin Fowler Signature Series, Addison-Wesley, 2007 For the rest of the lecture, we will highlight 4 Unit testing patterns: 4 stage testing pattern Inner class test pattern Reflection test pattern Test exception pattern.
44
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 44 Where are we now? Unit testing with jUnit (Example) Mock object pattern (Example) Dependency injection pattern (not only a testing pattern) Unit testing patterns 4 stage testing pattern Inner class test pattern Reflection test pattern Test exception pattern
45
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 45 Four-Stage Testing Pattern In the Four-Stage Testing Pattern a test driver (actor) executes a flow of events to interact with the SUT The flow of events can be into decomposed into four distinct phases that are executed in sequence. These phases are called setup, exercise, result validation and teardown 1. Setup In this phase, we set up a socalled test fixture. In the test fixture we set up state and behavior that is needed to observe the SUT (such as using a mock object) 2. Run the Test In this phase, we interact with the SUT, for example by calling a method (Often a little bit more Setup is required before calling the method) 3. Validate We look at the results with respect to state and/or behavior of the test and determine whether the observed outcome is equal to the expected outcome. (Note: In the XP literature the incorrect term Verify is often used. Verification is not the same as validation) 4. Teardown We put the SUT back into the state before the test was executed. In particular, we have to tear down any object we had to instantiate in the test fixture.
46
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 46 SUT (used in the following Slides) public class Authentication { private String key; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String encodePassword(String password) throws ValidationException { String encodedPassword = ""; if (password == null || password.length() == 0) { throw new ValidationException("Password is empty"); } // do the encoding return encodedPassword; } Assume we want to unit test the method encodePassword().
47
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 47 The 4 Phases: Setup, Exercise, Validate, Teardown public class AuthEncodingTest { Authentication authenticator; String name; @Before public void setUp() { authenticator = new Authentication(); authenticator.setKey("TESTKEY"); name = "user"; } @After public void tearDown() { authenticator.setKey(""); } @Test public void encoding() throws ValidationException { String expectedKey = "fwe94t-gft5"; String observedKey = authenticator.encodePassword(name); assertEquals(expectedKey, observedKey); } 1.A) Setup prerequisite objects: Declaration and initialization of authenticator key and user name 2. Run the test: Call e ncodePassword( ), the method being tested 3. Evaluate the results: For "user" and "TESTKEY" key we expect to get the key fwe94t-gft5" 4. Tear down the prerequisite objects 1.B) Continued setup of prerequisite objects: expected key The Test method
48
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 48 Outline of the Lecture Unit testing with jUnit (Example) Mock object pattern (Example) Dependency injection pattern (not only a testing pattern) Unit testing patterns 4 stage testing pattern Inner class test pattern Reflection test pattern Test exception pattern
49
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 49 Inner Class Test Pattern: Testing a protected method Problem: For the test a protected entity (field or method) needs to be accessed from another package Assume the encodePassword() method from the previous example is protected
50
Inner Class Test Pattern: Testing a protected method Solution: Introduce an anonymous inner class extending the original class and offer a public method delegating to the protected method of the original class ✔ public class AuthEncodingTest {... class TestAuthentication extends Authentication { public String callEncodePassword(String password) throws ValidationException { // We can call a protected method from here return encodePassword(password); } @Test public void encoding() throws ValidationException { TestAuthentication authenticator = new TestAuthentication(); authenticator.setKey("TESTKEY"); String expectedKey = "fwe94t@#$5“; String name = "user"; // call the tested method by means of the Inner Class assertEquals(expectedKey, authenticator.callEncodePassword(name)); }... }
51
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 51 Outline of the Lecture Unit testing with jUnit (Example) Mock object pattern (Example) Dependency injection pattern (not only a testing pattern) 4 stage testing pattern Inner class test pattern Reflection test pattern Test exception pattern
52
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 52 Reflection Test Pattern: Testing a Private Attribute Problem: There are some cases when it is necessary to test a private attribute Solution: Use reflection Example: Assume there is no getter for the private field “key” of the Authentication class but we need to access it for our test public class AuthPrivacyTest { @Test public void testKey() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Authentication auth = new Authentication(); String privateKey = "privateKey"; auth.setKey(privateKey); Class cl = auth.getClass(); // get the reflected object Field field = cl.getDeclaredField("key"); // set accessible true field.setAccessible(true); assertEquals(field.get(auth), privateKey); } Get the class object for Authentication Get the field “key” Set the field to be accessible
53
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 53 Outline of the Lecture Unit testing with jUnit (Example) Mock object pattern (Example) Dependency injection pattern (not only a testing pattern) Unit Test patterns 4 stage testing pattern Inner class test pattern Reflection test pattern Test exception pattern
54
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 54 Test Exception Pattern Problem: How to set up a test which is expected to result in an exception being raised The Authentication class for instance is expected to raise a ValidationException when confronted with an empty password Solution: The testing framework provides a mechanism for expecting exceptions Example: public class AuthExceptionTest { Authentication authenticator; @Before public void setUp() { authenticator = new Authentication(); } @Test(expected = ValidationException.class) public void encoding() throws ValidationException { authenticator.encodePassword(null); } jUnit provides a mechanism for doing exception testing
55
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 55 Readings Brown & Tapolcsanyi: Mock Object Patterns. In Proceedings of the 10th Conference on Pattern Languages of Programs, 2003. Published online: http://hillside.net/plop/plop2003/papers.html http://hillside.net/plop/plop2003/papers.html EasyMock Framework http://easymock.org/, Last Viewed: Dec 2009http://easymock.org/ Martin Fowler: Inversion of Control Containers and the Dependency Injection pattern. http://martinfowler.com/articles/injection.html, Last Updated: 23 Jan 2004http://martinfowler.com/articles/injection.html Google Guice Framework http://code.google.com/p/google-guice/, Last Viewed: Dec 2009http://code.google.com/p/google-guice/ Gerard Meszaros: xUnit Test Patterns – Refactoring Test Code. Martin Fowler Signature Series, Addison-Wesley, 2007 Typemock Ltd. Unit-Test Patterns for.NET - Part I. http://www.typemock.com/Docs/Unit_Test_Patterns_for_NET_Development_Part- 1.php, Last Update: July 2008 http://www.typemock.com/Docs/Unit_Test_Patterns_for_NET_Development_Part- 1.php Johnson, R. and Foote, B.: Designing Reusable Classes. Journal of Object Oriented Programming 1, 2. SIGS Publication Group, (June/July 1988), pp22-35.
56
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 56 Additional Slides
57
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 57 History of Dependency Injection The basic idea behind dependency injection was was introduced by Johnson and Foote in 1988 in their paper “Designing Reusable Classes” In their section on frameworks they write: One important characteristic of a framework is that the methods defined by the user to tailor the framework will often be called from within the framework itself, rather than from the user's application code The framework often plays the role of the main program in coordinating and sequencing application activity. This inversion of control gives frameworks the power to serve as extensible skeletons The methods supplied by the user tailor the generic algorithms defined in the framework for a particular application The inversion of control principle was used by Martin Fowler who coined the term dependency injection in 2004. http://martinfowler.com/articles/injection.html
58
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 58 Additional Readings Rob Kuijt's Testing Blog about Test Design Patterns http://robkuijt.nl/ in particular http://robkuijt.nl/testdesignpatterns/index.phphttp://robkuijt.nl/ http://robkuijt.nl/testdesignpatterns/index.php John D. McGregor, The Observer Test Patternhttp://www.cs.clemson.edu/~johnmc/joop/col18/col umn18.htmlhttp://www.cs.clemson.edu/~johnmc/joop/col18/col umn18.html Martin Fowler, Dependency Injection http://martinfowler.com/articles/injection.html http://martinfowler.com/articles/injection.html
59
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 59 3 Additional Testing Patterns Unit testing with jUnit (Example) Mock object pattern (Example) Dependency injection pattern (not only a testing pattern) Unit Test patterns 4 stage testing pattern Inner class test pattern Reflection test pattern Test exception pattern Observer Test pattern Two MVC Test Patterns View-State Test Pattern Model-State Test Pattern
60
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 60 Taxonomy for Testing Patterns
61
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 61 Observer Test Pattern The pattern is a variant of the observer pattern The test software needs to be notified when an action has happened. The observer test pattern allows a SUT using the observer pattern to be tested without being modified. The design describes a relationship between the application software and the test software. The pattern describes a set of interactions The Subject object treats the observer test object like any other observer, The Observer test object then accesses attributes of the Subject.
62
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 62 Observer Test Pattern (Warning: This slide is work in Progress) Applicable when the test needs to be notified when an action has happened or a state has been changed Assumption: The SUT is already using the observer pattern The observer test pattern just adds an ObserverTes t class to the test model The Subject object treats the ObserverTest object like any other observer even though it is in the Test Model The ObserverTest object can access any attributes and any method of the Subject in the SUT! SUT Additional Object in In the Test Model Test model
63
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 63 Observer Test Pattern: Test model Object is accessing the SUT(Sequence Diagram) Work in Progress, To be done
64
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 64 Two Testing Patterns for MVC Recall the MVC Architectural Pattern From http://www.codeproject.com/KB/architecture/autp5.aspx#The%20Simple- Test%20Pattern3 http://www.codeproject.com/KB/architecture/autp5.aspx#The%20Simple- Test%20Pattern3 Two MVC Test Patterns View-State Test Pattern Model-State Test Pattern
65
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 65 The View-State Test Pattern This pattern tests that when the model state changes, the view changes state appropriately This test exercises only half of the MVC pattern--the model event notifications to the view, and the view management of those events. The controller is not tested in this test pattern.
66
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 66 The Model-State Test Pattern This pattern simulates user input by invoking a state change event such as "KeyUp", "Click", etc. The test pattern validates that the model state is changed appropriately and that the expected events fired. The test may require some setup on the model itself. It also treats the controller as a black box, however model state can be inspected to determine whether the controller is managing the model state appropriately.
67
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 67 jUnit 4 vs jUnit 3 import junit.framework.TestCase; public class CalculatorTest extends TestCase { public void testadd() {.... } import org.junit.Test; public class CalculatorTest { @Test public void add() {.... } 67 jUnit 4 is completely based on annotations No longer necessary to extend class TestCase Test-method names do not have to start with the prefix test. Instead they are annotated with the @Test annotation jUnit 3 jUnit 4
68
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 68 Testing for Exceptions (jUnit 4 vs jUnit 3) public class CalculatorTest { public void testDivideByZero() { try { int n = 2/0; fail(„Division by zero!“); } catch (ArithmeticException success { assertNotNull(Success.getMessage()) } public class CalculatorTest { @Test(expected=ArithmeticException.class) public void DivideByZero() { int n = 2/0; } 68 jUnit 3jUnit 4 jUnit 3: Wrapping a try block around the code that throws the exception. Bad readability. In jUnit 4, one can write the code that throws the exception and use an annotation to declare that the exception is expected.
69
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 69 Writing Fixtures and Test Cases in JUnit 3.0 public class MyListTestCase extends TestCase { // … private MyList aList; private String anElement; public void setUp() { aList = new MyList(); anElement = “a string”; } public void testAdd() { aList.add(anElement); assertTrue(aList.size() == 1); assertTrue(aList.contains(anElement)); } public void testRemove() { aList.add(anElement); aList.remove(anElement); assertTrue(aList.size() == 0); assertFalse(aList.contains(anElement)); } Test Fixture Test Case
70
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 70 Test Suites in jUnit 3.0 public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest(new MyListTest(“testAdd”)); suite.addTest(new MyListTest(“testRemove”)); return suite; } Test run(TestResult) TestCase run(TestResult) setUp() tearDown() testName:String runTest() TestSuite run(TestResult) addTest() Composite Pattern! *
71
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 71 Writing TestCases in JUnit 3.0 public class MyListTestCase extends TestCase { public MyListTestCase(String name) { super(name); } public void testAdd() { // Set up the test List aList = new MyList(); String anElement = “a string”; // Perform the test aList.add(anElement); // Check if test succeeded assertTrue(aList.size() == 1); assertTrue(aList.contains(anElement)); } protected void runTest() { testAdd(); } Test run(TestResult) MyListTestCase setUp() tearDown() runTest() testAdd() testRemove() TestResult TestCase run(TestResult) setUp() tearDown() testName:String runTest() TestSuite run(TestResult) addTest() MyList add() remove() contains() size() *
72
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 72 Example Code package income; public enum Position { BOSS, PROGRAMMER, SURFER } package income.exceptions; public class PositionException extends RuntimeException { private static final long serialVersionUID = 1L; public PositionException(String message) { super(message); } package income.exceptions; public class CalcMethodException extends RuntimeException { private static final long serialVersionUID = 1L; public CalcMethodException(String message) { super(message); }
73
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 73 Test run(TestResult) MyListTestCase setUp() tearDown() runTest() testAdd() testRemove() TestResult TestCase run(TestResult) setUp() tearDown() testName:String runTest() TestSuite run(TestResult) addTest() MyList add() remove() contains() size() * SUT (Unit to be tested) MyList Methods under test add() remove() contains() size() Concrete Test case MyListTestCase testAdd testRemove
74
Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 74 GOF Design patterns used in the 4-Stage Test Pattern in JUnit 3.0 Test run(TestResult) ConcreteTestCase setUp() tearDown() runTest() TestResult TestCase run(TestResult) setUp() tearDown() testName:String runTest() TestSuite run(TestResult) addTest() Command Pattern Composite Pattern Adapter Pattern Template Method Pattern SUT *
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.