Using UML Patterns and Java ObjectOriented Software Engineering

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.

Recall: The System Model for the Simple Inventory System Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 2

Java Code for the Simple Inventory System public class Inventory. System { private static String TALISKER = "Talisker"; private int total. Stock; private Warehouse warehouse = new Warehouse. Impl(); public void add. To. Warehouse(String item, int amount) { warehouse. add(item, amount); total. Stock += amount; } public int get. Total. Stock() { return total. Stock; } public boolean process. Order(Order order) { order. fill(warehouse); Note in this example the UML Client method use. Inventory. System() is realized as the main() method in the class Inventory. System return order. is. Filled(); } public static void main(String[] args) { Inventory. System inventory. System = new Inventory. System(); inventory. System. add. To. Warehouse(TALISKER, 50); boolean order 1 success = inventory. System. process. Order(new Order. Impl(TALISKER, 50)); boolean order 2 success = inventory. System. process. Order(new Order. Impl(TALISKER, 51)); ? System. out. println("Order 1 succeeded? " + order 1 success + " - Order 2 succeeded? " + order 2 success); } }

Java Code for the Warehouse public interface Warehouse { public boolean has. Inventory(String item, int amount); public int get. Inventory(String item); public void add(String item, int amount); public void remove(String item, int amount); } public class Warehouse. Impl implements Warehouse { private Map<String, Integer> inventory = new Hash. Map<String, Integer>(); public boolean has. Inventory(String item, int amount) { return inventory. get(item) >= amount; } public int get. Inventory(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); } }

Java Code for the Order public interface Order { public boolean is. Filled(); public void fill(Warehouse warehouse); } public class Order. Impl implements Order { private String item; private int amount; private boolean filled = false; public Order. Impl(String item, int amount) { this. item = item; this. amount = amount; } public boolean is. Filled() { return filled; } public void fill(Warehouse warehouse) { if(warehouse. has. Inventory(item, amount)) { warehouse. remove(item, amount); filled = true; } } }

Test Model of the Inventory. System • Assume we now want to unit test the class Order, our SUT • Note: Order is not unit testable in isolation, it depends on Warehouse Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 6

Unit testing Order with j. Unit public class Order. State. Tester { private static String TALISKER = "Talisker"; private Warehouse warehouse; Needed for unit testing Order: Instantiation of Warehouse. Impl @Before public void set. Up() { warehouse = new Warehouse. Impl(); warehouse. add(TALISKER, 50); } @Test public void order. Is. Filled. If. Enough. In. Warehouse() { Order order = new Order(TALISKER, 50); order. fill(warehouse); assert. True(order. is. Filled()); assert. Equals(0, warehouse. get. Inventory(TALISKER)); } @Test public void order. Does. Not. Remove. If. Not. Enough() { Order order = new Order(TALISKER, 51); order. fill(warehouse); + assert. False(order. is. Filled()); assert. Equals(50, warehouse. get. Inventory(TALISKER)); } } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 7

From State Testing to Behavior Testing • Observation: j. Unit 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: • j. Unit 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. Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 8

Outline of the Lecture • • Unit testing with j. Unit (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 Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 9

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. Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 10

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 object • 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) Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 11

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 10 th Conference on Pattern Languages of Programs, 2003. http: //hillside. net/plop 2003/papers. html Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 12

Frameworks that implement Mock Objects • j. Mock Java • Easy. Mock • Mock. PP C++ • Google. Mock • Test: : Mock. Object • RSpec Perl Ruby • See a more comprehensive list of implementations at http: //www. mockobjects. com/ • In the following examples, we use Easy. Mock. Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 13

The Easy. Mock Framework • The goal of the Easy. Mock framework is to make it easy to deal with mock objects • Easy. Mock operates using static method imports (org. easymock. Easy. Mock. *) • It uses a record/replay metaphor • Necessary steps in using the Easy. Mock framework: 1. Create the mock object for the interface of the collaborator (create. Mock(Class interface)) 2. Set its state and/or record expected behavior (expect(T method. Call). and. Return(T return. Value)) 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 Easy. Mock see: http: //easymock. org/ Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 14

Test Model of the Inventory. System with a Mock Object warehouse. Mock supplied by Easy. Mock Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 15

Testcase using the Mock Object Pattern Unit test filling. Removes. Inventory. If. In. Stock: 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: Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 16

Unit Test: filling. Removes. Inventory. If. In. Stock public class Order. Interaction. Tester. Easy. Mock { private static String TALISKER = "Talisker"; private Warehouse warehouse. Mock; @Test public void filling. Removes. Inventory. If. In. Stock() { 1. Create mock object Order order = new Order. Impl(TALISKER, 50); warehouse. Mock = create. Mock(Warehouse. class); 2. Specify expected behavior (“behavioral oracle”) expect(warehouse. Mock. has. Inventory(TALISKER, 50)). and. Return(true); warehouse. Mock. remove(TALISKER, 50); replay(warehouse. Mock); 3. Set mock object to be ready to play order. fill(warehouse. Mock); assert. True(order. is. Filled()); verify(warehouse. Mock); 4. Validate observed behavior against expected behavior } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 17

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, …). Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 18

Where are we now? üUnit testing with j. Unit (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 Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 19

Problem: Unit testing the Inventory. System • Recall the Inventory. System public class Inventory. System { private static String TALISKER = "Talisker“; private int total. Stock; private Warehouse warehouse = new Warehouse. Impl(); … } • Since the Inventory. System explicitly calls new to instantiate a specific implementation of Warehouse, we cannot test this Inventory. System implementation with a mock Warehouse • Can the Inventory. System implementation be changed to work with both, a production implementation of Warehouse and a mock Warehouse? ➞ Dependency Injection Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 20

Instantiation with new • What is wrong with the usual way of instantiating objects with new? 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? • new creates a direct compile time dependency on the implementation ! 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 • 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? ? Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 23

Instantiation with Factories • This removes the dependency on the implementation • But now there is a new association to Service. Factory, which provides access to the subclasses Service. Impl and Mock. Service. Impl • Service. Factory 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. ? ? ? ! Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 24

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; } … } 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: … a setter: public class Client { private Service service; public Client(Service service) { public void set. Service(Service service) { this. service=service; } } … … } } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 26

Introduction to Dependency Injection • This is what we have with factories: • 3 additional associations that increase the coupling between objects • High coupling is bad! Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 28

Introduction to Dependency Injection • This is what we want: • Client only depends on the interface Service • Low coupling! Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 30

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. Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 31

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 • Some examples for Java are: • J 2 EE 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. Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 32

Example: Existing Inventorysystem public class Inventory. System { private static String TALISKER = "Talisker"; private int total. Stock; private Warehouse warehouse = new Warehouse. Impl(); public void add. To. Warehouse(String item, int amount) { warehouse. add(item, amount); total. Stock += amount; } public int get. Total. Stock() { return total. Stock; } public boolean process. Order(Order order) { order. fill(warehouse); return order. is. Filled(); } public static void main(String[] args) { Inventory. System inventory. System = new Inventory. System(); inventory. System. add. To. Warehouse(TALISKER, 50); boolean order 1 success = inventory. System. process. Order(new Order. Impl(TALISKER, 50)); boolean order 2 success = inventory. System. process. Order(new Order. Impl(TALISKER, 51)); System. out. println("Order 1 succeeded? " + order 1 success + " - Order 2 succeeded? " + order 2 success); } } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 33

Example: Inventory. System with Guice public class Inventory. System. DI { private static String TALISKER = "Talisker"; private int total. Stock; @Inject private Warehouse warehouse; public void add. To. Warehouse(String item, int amount) { warehouse. add(item, amount); total. Stock += amount; } public int get. Total. Stock() { Can you spot the difference to the old code? return total. Stock; } public boolean process. Order(Order order) { order. fill(warehouse); return order. is. Filled(); } public static void main(String[] args) { Injector injector = Guice. create. Injector(new Production. Module()); Inventory. System. DI inventory. System = injector. get. Instance(Inventory. System. DI. class); inventory. System. add. To. Warehouse(TALISKER, 50); boolean order 1 success = inventory. System. process. Order(new Order. Impl(TALISKER, 50)); boolean order 2 success = inventory. System. process. Order(new Order. Impl(TALISKER, 51)); System. out. println("Order 1 succeeded? " + order 1 success + " - Order 2 succeeded? " + order 2 success); } } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 34

Can you spot the difference to the old one? public class Inventory. System. DI { Notice there is no “new” here anymore! private static String TALISKER = "Talisker"; private int total. Stock; @Inject private Warehouse warehouse; public void add. To. Warehouse(String item, int amount) { warehouse. add(item, amount); total. Stock += amount; } What is an injector? public int get. Total. Stock() { return total. Stock; } public boolean process. Order(Order order) { order. fill(warehouse); return order. is. Filled(); Here an injector is created that allows to specify bindings of implementations to interfaces. Uses a Production. Module. See slide 41 } These are the changes to the “Client” public static void main(String[] args) { Injector injector = Guice. create. Injector(new Production. Module()); Inventory. System. DI inventory. System = injector. get. Instance(Inventory. System. DI. class); inventory. System. add. To. Warehouse(TALISKER, 50); boolean order 1 success = inventory. System. process. Order(new Order. Impl(TALISKER, 50)); boolean order 2 success = inventory. System. process. Order(new Order. Impl(TALISKER, 51)); System. out. println("Order 1 succeeded? " + order 1 success + " - Order 2 succeeded? " + order 2 success); } } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 35

What is an Injector? How do you get the Jam into the Krapfen? Inventory. System Bernd Bruegge & Allen H. Dutoit Injector filled with “Jam” (i. e. Warehouse Object-Oriented Software Engineering: Using UML, Patterns, and Java 36

Guice Modules: Configure the Bindings • Guice uses the concept of a Module to bind a subclass implementation to an interface • Here is the Guice Module Production. Module that binds Warehouse. Impl to Warehouse: public class Production. Module implements Module { public void configure(Binder binder) { binder. bind(Warehouse. class). to(Warehouse. Impl. class); } } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 37

4 Steps to use Guice 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: Inventory. System) For more details: http: //code. google. com/p/google-guice/wiki/User. Guide Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 38

Inventory. System with Guice Code public class Inventory. System. DI { private static String TALISKER = "Talisker"; private int total. Stock; @Inject private Warehouse warehouse; public void add. To. Warehouse(String item, int amount) { warehouse. add(item, amount); total. Stock += amount; } public int get. Total. Stock() { return total. Stock; } public boolean process. Order(Order order) { order. fill(warehouse); return order. is. Filled(); } public static void main(String[] args) { Injector injector = Guice. create. Injector(new Production. Module()); Inventory. System. DI inventory. System = injector. get. Instance(Inventory. System. DI. class); inventory. System. add. To. Warehouse(TALISKER, 50); boolean order 1 success = inventory. System. process. Order(new Order. Impl(TALISKER, 50)); boolean order 2 success = inventory. System. process. Order(new Order. Impl(TALISKER, 51)); System. out. println("Order 1 succeeded? " + order 1 success + " - Order 2 succeeded? " + order 2 success); } } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 39

Guice and Unit Testing • How does this help us with unit testing? • Remember – instead of this hard-coded dependency: public class Inventory. System { private Warehouse warehouse = new Warehouse. Impl(); … } • we now have: Injector filled with Warehouse public class Inventory. System { @Inject private Warehouse warehouse; … Inventory. System } • 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 Test. Module. Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 40

Configuring the Bindings • The Guice module for the production code: public class Production. Module implements Module { public void configure(Binder binder) { binder. bind(Warehouse. class). to(Warehouse. Impl. class); } } • The Guice module for the unit test: public class Test. Module implements Module { public void configure(Binder binder) { Warehouse warehouse. Mock = Easy. Mock. create. Mock(Warehouse. class); binder. bind(Warehouse. class). to. Instance(warehouse. Mock); } } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 41

Unit Test of the Inventory. System using Guice and Easy. Mock public class Inventory. System. DITest { private static String TALISKER = "Talisker"; private Inventory. System. DI inventory. System; private Injector injector; Here we tell Guice to create an injector using Test. Module, that binds Warehouse to a Warehouse Mock @Before public void set. Up() throws Exception { injector = Guice. create. Injector(new Test. Module()); inventory. System = injector. get. Instance(Inventory. System. DI. class); } @Test public void add. To. Warehouse() { Warehouse warehouse. Mock = injector. get. Instance(Warehouse. class); We let Guice instantiate the Inventory. System and the Warehouse in it warehouse. Mock. add(TALISKER, 50); Easy. Mock. replay(warehouse. Mock); inventory. System. add. To. Warehouse(TALISKER, 50); assert. Equals(inventory. System. get. Total. Stock(), 50); Easy. Mock. verify(warehouse. Mock); } Here we retrieve the mock Warehouse from the injector to use it during the test. } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 42

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: x. Unit 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. Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 43

Where are we now? üUnit testing with j. Unit (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 Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 44

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. Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 45

SUT (used in the following Slides) Assume we want to unit test the method encode. Password(). public class Authentication { private String key; public String get. Key() { return key; } public void set. Key(String key) { this. key = key; } public String encode. Password(String password) throws Validation. Exception { String encoded. Password = ""; if (password == null || password. length() == 0) { throw new Validation. Exception("Password is empty"); } // do the encoding return encoded. Password; } } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 46

The 4 Phases: Setup, Exercise, Validate, Teardown 1. A) Setup prerequisite objects: Declaration and initialization of authenticator key and user name public class Auth. Encoding. Test { Authentication authenticator; String name; @Before public void set. Up() { authenticator = new Authentication(); authenticator. set. Key("TESTKEY"); name = "user"; } 4. Tear down the prerequisite objects @After public void tear. Down() { authenticator. set. Key(""); The Test method } @Test public void encoding() throws Validation. Exception { 1. B) Continued setup of prerequisite objects: expected key String expected. Key = "fwe 94 t-gft 5"; String observed. Key = authenticator. encode. Password(name); assert. Equals(expected. Key, observed. Key); } } Bernd Bruegge & Allen H. Dutoit 3. Evaluate the results: For "user" and "TESTKEY" key we expect to get the key fwe 94 t-gft 5" Object-Oriented Software Engineering: Using UML, Patterns, and Java 2. Run the test: Call encode. Password(), the method being tested 47

Outline of the Lecture üUnit testing with j. Unit (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 Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 48

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 encode. Password() method from the previous example is protected Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 49

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 Auth. Encoding. Test {. . . class Test. Authentication extends Authentication { public String call. Encode. Password(String password) throws Validation. Exception { // We can call a protected method from here return encode. Password(password); } } @Test public void encoding() throws Validation. Exception { Test. Authentication authenticator = new Test. Authentication(); authenticator. set. Key("TESTKEY"); String expected. Key = "fwe 94 t@#$5“; String name = "user"; // call the tested method by means of the Inner Class assert. Equals(expected. Key, authenticator. call. Encode. Password(name)); }. . . }

Outline of the Lecture üUnit testing with j. Unit (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 Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 51

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 Auth. Privacy. Test { @Test public void test. Key() throws Security. Exception, No. Such. Field. Exception, Illegal. Argument. Exception, Illegal. Access. Exception { Authentication auth = new Authentication(); Get the class object String private. Key = "private. Key"; for Authentication auth. set. Key(private. Key); Class<? extends Authentication> cl = auth. get. Class(); Get the field “key” // get the reflected object Field field = cl. get. Declared. Field("key"); // set accessible true Set the field to be field. set. Accessible(true); accessible assert. Equals(field. get(auth), private. Key); } } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 52

Outline of the Lecture üUnit testing with j. Unit (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 Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 53

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 Validation. Exception when confronted with an empty password • Solution: The testing framework provides a mechanism for expecting exceptions • Example: public class Auth. Exception. Test { Authentication authenticator; @Before public void set. Up() { authenticator = new Authentication(); } } j. Unit provides a mechanism for doing exception testing @Test(expected = Validation. Exception. class) public void encoding() throws Validation. Exception { authenticator. encode. Password(null); } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 54

Readings • Brown & Tapolcsanyi: Mock Object Patterns. In Proceedings of the 10 th Conference on Pattern Languages of Programs, 2003. Published online: http: //hillside. net/plop 2003/papers. html • Easy. Mock Framework http: //easymock. org/, Last Viewed: Dec 2009 • Martin Fowler: Inversion of Control Containers and the Dependency Injection pattern. http: //martinfowler. com/articles/injection. html, Last Updated: 23 Jan 2004 • Google Guice Framework http: //code. google. com/p/google-guice/, Last Viewed: Dec 2009 • Gerard Meszaros: x. Unit 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 • Johnson, R. and Foote, B. : Designing Reusable Classes. Journal of Object Oriented Programming 1, 2. SIGS Publication Group, (June/July 1988), pp 22 -35. Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 55

Additional Slides Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 56

History of Dependency Injection • The basic idea behind dependency injection 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 Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 57

Additional Readings • Rob Kuijt's Testing Blog about Test Design Patterns http: //robkuijt. nl/ in particular http: //robkuijt. nl/testdesignpatterns/index. php • John D. Mc. Gregor, The Observer Test Patternhttp: //www. cs. clemson. edu/~johnmc/joop/col 18/col umn 18. html • Martin Fowler, Dependency Injection http: //martinfowler. com/articles/injection. html Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 58

3 Additional Testing Patterns üUnit testing with j. Unit (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 Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 59

Taxonomy for Testing Patterns Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 60

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. Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 61

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 SUT • The observer test pattern just adds an Observer. Test class to the test model • The Subject object treats the Observer. Test object like any other observer even though it is in the Test Model • The Observer. Test object can access any attributes and any method of the Subject in the SUT! Test model Additional Object in In the Test Model Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 62

Observer Test Pattern: Test model Object is accessing the SUT(Sequence Diagram) • Work in Progress, To be done Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 63

Two Testing Patterns for MVC • Recall the MVC Architectural Pattern • Two MVC Test Patterns • View-State Test Pattern • Model-State Test Pattern • Fromhttp: //www. codeproject. com/KB/architecture/autp 5. aspx#The%20 Simple. Test%20 Pattern 3 Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 64

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. Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 65

The Model-State Test Pattern • This pattern simulates user input by invoking a state change event such as "Key. Up", "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. Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 66

j. Unit 4 vs j. Unit 3 • j. Unit 4 is completely based on annotations • No longer necessary to extend class Test. Case • Test-method names do not have to start with the prefix test. • Instead they are annotated with the @Test annotation j. Unit 4 j. Unit 3 import org. junit. Test; import junit. framework. Test. Case; public class Calculator. Test { public class Calculator. Test extends Test. Case { @Test public void add() { public void testadd() { . . } } Bernd Bruegge & Allen H. Dutoit . . } } Object-Oriented Software Engineering: Using UML, Patterns, and Java 67 6 7

Testing for Exceptions (j. Unit 4 vs j. Unit 3) j. Unit 4 j. Unit 3 public class Calculator. Test { @Test(expected=Arithmetic. Exception. class) public void test. Divide. By. Zero() { public void Divide. By. Zero() { try { int n = 2/0; } fail(„Division by zero!“); } } catch (Arithmetic. Exception success { assert. Not. Null(Success. get. Message()) } } In j. Unit 4, one can write the code that throws the exception and use an annotation to declare that the exception is expected. Bernd Bruegge & Allen H. Dutoit } j. Unit 3: Wrapping a try block around the code that throws the exception. Bad readability. Object-Oriented Software Engineering: Using UML, Patterns, and Java 68 6 8

Writing Fixtures and Test Cases in JUnit 3. 0 public class My. List. Test. Case extends Test. Case { // … private My. List a. List; private String an. Element; Test Fixture public void set. Up() { a. List = new My. List(); an. Element = “a string”; } public void test. Add() { a. List. add(an. Element); assert. True(a. List. size() == 1); Test Case assert. True(a. List. contains(an. Element)); } public void test. Remove() { a. List. add(an. Element); Test Case a. List. remove(an. Element); assert. True(a. List. size() == 0); assert. False(a. List. contains(an. Element)); } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 69

Test Suites in j. Unit 3. 0 public static Test suite() { Test. Suite suite = new Test. Suite(); suite. add. Test(new My. List. Test(“test. Add”)); suite. add. Test(new My. List. Test(“test. Remove”)); return suite; } * Test Composite Pattern! run(Test. Result) Test. Suite Test. Case test. Name: String run(Test. Result) set. Up() tear. Down() run. Test() Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java run(Test. Result) add. Test() 70

Writing Test. Cases in JUnit 3. 0 public class My. List. Test. Case extends Test. Case { public My. List. Test. Case(String name) { * super(name); Test. Result } public void test. Add() { run(Test. Result) // Set up the test List a. List = new My. List(); String an. Element = “a string”; Test. Case test. Name: String run(Test. Result) set. Up() tear. Down() run. Test() // Perform the test a. List. add(an. Element); // Check if test succeeded assert. True(a. List. size() == 1); assert. True(a. List. contains(an. Element)); } My. List. Test. Case set. Up() tear. Down() run. Test() test. Add() test. Remove() protected void run. Test() { test. Add(); } } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 71 Test. Suite run(Test. Result) add. Test() My. List add() remove() contains() size()

Example Code package income; package income. exceptions; public enum Position { public class Calc. Method. Exception extends Runtime. Exception { private static final long serial. Version. UID = 1 L; BOSS, PROGRAMMER, SURFER } public Calc. Method. Exception(String message) { package income. exceptions; public class Position. Exception extends Runtime. Exception { private static final long serial. Version. UID = 1 L; public Position. Exception(String message) { super(message); } } Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 72

* Test. Result • SUT (Unit to be tested) run(Test. Result) • My. List • Methods under test • • add() remove() contains() size() Test. Case • Concrete Test case • My. List. Test. Case • test. Add • test. Remove Test. Suite test. Name: String run(Test. Result) set. Up() tear. Down() run. Test() run(Test. Result) add. Test() My. List. Test. Case add() remove() contains() size() set. Up() tear. Down() run. Test() test. Add() test. Remove() Bernd Bruegge & Allen H. Dutoit Object-Oriented Software Engineering: Using UML, Patterns, and Java 73

GOF Design patterns used in the 4 -Stage Test Pattern in JUnit 3. 0 * Test. Result Command Pattern run(Test. Result) Composite Pattern Template Method Pattern Adapter Pattern Bernd Bruegge & Allen H. Dutoit Test. Case test. Name: String run(Test. Result) set. Up() tear. Down() run. Test() Test. Suite run(Test. Result) add. Test() SUT Concrete. Test. Case set. Up() tear. Down() run. Test() Object-Oriented Software Engineering: Using UML, Patterns, and Java 74
- Slides: 72