Test Isolation and Mocking

Slides:



Advertisements
Similar presentations
SOFTWARE TESTING. INTRODUCTION  Software Testing is the process of executing a program or system with the intent of finding errors.  It involves any.
Advertisements

(Quickly) Testing the Tester via Path Coverage Alex Groce Oregon State University (formerly NASA/JPL Laboratory for Reliable Software)
Pragmatic Application Building: Step by Step Jay Sissom Principal Systems Analyst Indiana University
Unit Testing Tips and Tricks: Database Interaction Louis Thomas.
Options for User Input Options for getting information from the user –Write event-driven code Con: requires a significant amount of new code to set-up.
Unit Testing & Defensive Programming. F-22 Raptor Fighter.
Testing. What is Testing? Definition: exercising a program under controlled conditions and verifying the results Purpose is to detect program defects.
CSC 213 – Large Scale Programming. Why Do We Test?
Designing For Testability. Incorporate design features that facilitate testing Include features to: –Support test automation at all levels (unit, integration,
Capture-Replay Mocks Scandinavian Developer Conference 4 th April 2011 Geoff Bache.
Junit At the forefront of Test Driven Development.
Test Isolation and Mocking Technion – Institute of Technology Author: Gal Lalouche © 1 Author: Gal Lalouche - Technion 2015 ©
Dependency Injection Technion – Institute of Technology Author: Gal Lalouche - Technion 2015 ©
1/20/03A2-1 CS494 Interfaces and Collection in Java.
(1) Unit Testing and Test Planning CS2110: SW Development Methods These slides design for use in lab. They supplement more complete slides used in lecture.
Using Mock Objects with Test Driven Development Justin Kohlhepp
Documentation. Your documentation must fit the needs of your audience. It’s always better to say one thing that is useful, as opposed to many things that.
Refactoring & Testability. Testing in OOP programming No life in flexible methodologies and for refactoring- infected developers without SOME kind of.
Mock objects.
Exceptions and Assertions Chapter 15 – CSCI 1302.
Dependency Injection Frameworks Technion – Institute of Technology Author: Assaf Israel - Technion 2013 ©
Copyright 2007 SpringSource. Copying, publishing or distributing without express written permission is prohibited. Testing Spring Applications Unit Testing.
Mock Me If You Can An Introduction to the Mocking Framework Mockito Symposium on Software Performance 2014 Christian Wulf ― Software Engineering.
Lecture IX: Testing Web Services with Mocking CS 4593 Cloud-Oriented Big Data and Software Engineering.
Unit Testing in Eclipse Presented by David Eisler 08/09/2014.
Dependency Injection with Guice Technion – Institute of Technology Author: Gal Lalouche - Technion 2016 ©
CS 440 Database Management Systems Stored procedures & OR mapping 1.
Module 9: Operator overloading #1 2000/01Scientific Computing in OOCourse code 3C59 Module 9: Operator Overloading In this module we will cover Overloading.
Test Isolation and Mocking Technion – Institute of Technology Author: Gal Lalouche © 1 Author: Gal Lalouche - Technion 2016 ©
Testing Technion – Institute of Technology Author: Gal Lalouche © 1 Author: Gal Lalouche - Technion 2016 ©
Powerpoint Templates Page 1 Powerpoint Templates Unit Testing Ari Seppi
11 Making Decisions in a Program Session 2.3. Session Overview  Introduce the idea of an algorithm  Show how a program can make logical decisions based.
Memory Management.
Class Invariants Class invariants are logical conditions to ensure the correct working of a class. Class invariants must hold true when an object is created,
Reference: Object Oriented Design and Programming (Horstmann)
Software Testing.
Eddie Jaquith Alexis Jarvis
Templates.
Designing For Testability
CSE 374 Programming Concepts & Tools
Design Patterns I Technion – Institute of Technology
Software Testing.
Test-Driven Development
UNIT - V STORED PROCEDURE.
Unit Testing in a Team Sparkhound Presents by Steve Schaneville
Week 4 - Monday CS222.
Unit testing C# classes
Topic 3: Data Binary Arithmetic.
Lesson 2: Building Blocks of Programming
History, Characteristics and Frameworks
Mocking Your Objects with Impunity
Model-View-Controller Patterns and Frameworks
What’s changed in the Shibboleth 1.2 Origin
Test-driven development (TDD)
Test patterns.
Lecture 16 - Interfaces Professor Adams.
CSCE 489- Problem Solving Programming Strategies Spring 2018
CMSC 202 Exceptions 2nd Lecture.
CMSC 202 Exceptions 2nd Lecture.
CS240: Advanced Programming Concepts
Chapter 6 – Methods Topics are:
CS 240 – Advanced Programming Concepts
Developing and testing enterprise Java applications
CMSC 202 Exceptions 2nd Lecture.
Designing For Testability
CMPE212 – Reminders Assignment 2 due next Friday.
CMSC 202 Exceptions 2nd Lecture.
Exception Handling.
Lecture 3 More on Flow Control, More on Functions,
Junit Tests.
Presentation transcript:

Test Isolation and Mocking Author: Gal Lalouche - Technion 2017 © Technion – Institute of Technology 236700 Author: Gal Lalouche ©

Mocking – motivation Initializing collaborators can include a lot of boiler plate, that is irrelevant to the test There may be bugs in the dependencies Or dependencies may not even be written yet! I/O operations (databases/files/sockets) in test subjects may slow down tests They might have nasty side effects or need to be cleaned up after each test Using concrete classes couples our tests with those classes Author: Gal Lalouche - Technion 2017 ©

Mocking - solution Solution: Replace dependencies with mock objects Simple dependencies that are 100% bug free Although bugs may appear in the mock objects configurations Easy to control their behavior Easy to assert their use by the tested class Warehouse DataBase db ==================== void process(Order o) SQL DB db.getProduct(o.productID); Production HashTable Testing Author: Gal Lalouche - Technion 2017 ©

Improved design This is another example where code testability improves its design Isolation forces the code to be: Modular Decoupled from its dependencies More coherent Today Testing Tomorrow Author: Gal Lalouche - Technion 2017 © PizzaOrder PizzaOrder PizzaOrder If you follow the notion that testing is specification, mocking "proves" that our class can work with any other dependency CreditCardProcessor Mock Processor Paypal Processor

Manual mocking: problems Writing mock objects can be a pain A lot of time is wasted writing an object that is only used for testing Configuration can be tricky Validation is hard Case Study – Shopping cart Shoppers can add/remove items from the shopping cart Each item is a complex object with many attributes The shopping cart is initialized with a ShoppingSuggestions which offer other purchases based on the shopping cart content Author: Gal Lalouche - Technion 2017 ©

Shopping cart: that thing Author: Gal Lalouche - Technion 2017 ©

Shopping cart: implementation We want to test the ShoppingCart class The ShoppingSuggestions is responsible for displaying product suggestions based on the shopping cart contents public class ShoppingCart { private final Map<Item, Integer> items = new HashMap<>(); private final ShoppingSuggestions suggestions; public ShoppingCart(ShoppingSuggestions suggestions) { this.suggestions = suggestions; } … Author: Gal Lalouche - Technion 2017 © This is a form of dependency injection, which we will see more of in the next couple of weeks

Case study – Shopping cart We would like to test the following two methods public void addItem(Item item) { int amount = 1; if (items.containsKey(item)) amount = items.get(item) + 1; items.put(item, amount); suggestions.displaySuggestionFor(item); } public void removeItem(Item item) { if (!items.containsKey(item)) return; int amount = items.get(item); if (amount == 0) { items.remove(item); suggestions.removeSuggestionFor(item); items.put(item, amount - 1); Author: Gal Lalouche - Technion 2017 © Bug: Should be 1

Manual mocking: implementation We want to isolate the subject class and replace arguments and dependencies with controlled objects (Mocks) We start with a naive mock of ShoppingSuggestions We will soon see this is not enough class FakeShoppingSuggestions implements ShoppingSuggestions { public boolean invoked = false; @Override public void removeSuggestionFor(Item item) { invoked = true; } public void displaySuggestionFor(Item item) { invoked = true; } } Author: Gal Lalouche - Technion 2017 ©

We don’t really care about these details Manual mocking (Cont.) Let’s look at our first test: @Test public void updateSuggestionWhenAddingItem() { FakeShoppingSuggestions ss = new FakeShoppingSuggestions(); ShoppingCart cart = new ShoppingCart(ss); Item someItem = new Item(3000, "laptop", "Lenovo", Category.COMPUTERS); cart.addItem(someItem); assertTrue(ss.invoked); } Author: Gal Lalouche - Technion 2017 © We would like to use the ShoppingSuggestions interface, to be sure we haven’t introduced bugs in the test… but we can’t because of the last assert. We don’t really care about these details This test doesn’t check how many times each method was invoked, and is susceptible to bugs in the mock object.

Manual mocking (Cont.) Our second test is even more problematic In order to deal with some of these problems we must write smarter (and possibly buggier) mock objects. @Test public void removeSuggestionWhenRemovingLastItem() { FakeShoppingSuggestions ss = new FakeShoppingSuggestions(); ShoppingCart cart = new ShoppingCart(ss); Item someItem = new Item(3000, "laptop", "Lenovo", Category.Computers); cart.addItem(someItem); ss.invoked = false; cart.removeItem(someItem); assertTrue(ss.invoked); } Author: Gal Lalouche - Technion 2017 © If we forget to clean ss.invoked the test will always pass We might end up having to test our mocks, which is probably not what we want We must update our mocking object throughout the test. What happens if we forget?

We simply ask for a ShoppingSuggestions and Item mocks Mockito (finally) We will use a Mocking Framework (Mockito) to create our mock objects, define their behavior and verify the results. We simply ask for a ShoppingSuggestions and Item mocks @Test public void updateSuggestionWhenAddingItem() { ShoppingSuggestions ss = Mockito.mock(ShoppingSuggestions.class); Item someItem = Mockito.mock(Item.class); ShoppingCart cart = new ShoppingCart(ss); cart.addItem(someItem); Mockito.verify(ss, Mockito.only()).displaySuggestionFor(someItem); } Author: Gal Lalouche - Technion 2017 © Notice the use of fluent API Mockito.only() isn't actually necessary since it's the default We can verify that the displaySuggestionFor() method will be called exactly once with the someItem argument.

Mockito: Error handling Let’s look at our second test: @Test public void removeSuggestionWhenRemovingLastItem() { ShoppingSuggestions ss = Mockito.mock(ShoppingSuggestions.class); Item someItem = Mockito.mock(Item.class); ShoppingCart cart = new ShoppingCart(ss); cart.addItem(someItem); Mockito.verify(ss, Mockito.only()).displaySuggestionFor(someItem); cart.removeItem(someItem); Mockito.verify(ss, Mockito.only()).removeSuggestionFor(someItem); } Author: Gal Lalouche - Technion 2017 © Wanted but not invoked: suggestions.removeSuggestionFor( Mock for Item, hashCode: 1992679988 ); -> at ShoppingCartTestWithMockito.testRemoveItem(ShoppingCartTestWithMockito.java:29)

Mockito: Configuration Using a Mocking Framework means we do not need to write a complete class to define the mock behavior: @Test public void testTotalAmount() { ShoppingSuggestions ss = Mockito.mock(ShoppingSuggestions.class); Item someItem = Mockito.mock(Item.class); ShoppingCart cart = new ShoppingCart(ss); Mockito.when(someItem.getPrice()).thenReturn(70); Mockito.when(someItem.getShippingCosts()).thenReturn(6); cart.addItem(someItem); Assert.assertEquals(76, cart.total()); } Author: Gal Lalouche - Technion 2017 © We only define what we’re interested in. We can also tell the mock to throw an exception.

When should you mock? You should mock when: You don’t want to mock: You don’t want to be tied down to a single implementation Using real objects is impossible or too expensive, e.g., I/O, networking, side effects, concurrency You don’t care about initializing collaborators You want complete control over collaborators behavior But fakes/stubs might be a better solution in those cases You don’t want to mock: Dependable dependencies: standard / external library classes For the sake of mocking Author: Gal Lalouche - Technion 2017 © Useful final classes include all the primitives, File, Scanner (reads from files), etc.

Mocking – Pros Easy to use and configure Mockito is more declarative than implementing collaborators on our own We are truly unit testing Our tests aren’t dependent on other classes Less noise when a dependant class breaks Mocks let us design against an interface, rather than an implementation Improve our design by reducing coupling and increasing cohesion Author: Gal Lalouche - Technion 2017 ©

Mocking – Cons Mocks are still harder to use than real classes For example, it's very hard to configure database mocks; need to answer queries, update state, etc. A more specific testing-oriented class could often be simpler to use than a mock Naturally white-box testing We are deeply coupled with the class’s implementation Implementation logic changes => tests break Overzealous use of mocks could mean no automatic integration tests Mocks let us test side-effects, but we should strive to make our code have no side-effect Author: Gal Lalouche - Technion 2017 ©

Mocking – Additional Reading Mocks aren’t Stubs Quick use guide Stubbing and Mocking with Mockito 2 and JUnit Mockito website Alternatives to Mockito: JMock, EasyMock, PowerMock Author: Gal Lalouche - Technion 2017 ©

Appendix Author: Gal Lalouche - Technion 2017 © Advanced Mocking

Mockito: Advanced matching Mockito supports multiple ways to verify arguments Match any argument Assert on argument Capture argument Mockito.verify(ss).addItem(ArgumentMatchers.any()); Mockito.verify(ss).addItem(ArgumentMatchers.argThat( item -> item.getName().equals("foobar")); Author: Gal Lalouche - Technion 2017 © Can also do this with several mock objects and verify order between them ArgumentCaptor<Item> captor = ArgumentCaptor.forClass(item.class); Mockito.verify(ss).addItem(captor.capture()); assertEquals(captor.getValue().getName(), "foobar");

Mockito: Advanced answers Mockito supports even more fine tuned configuration Returning multiple answers Specific arguments Full answer (similar to overriding) Spying (behaving like the original class – somwhat discouraged!) Item item = Mockito.mock(Item.class); Mockito.when(item.getId()).thenReturn(1, 2, 3, 4, 5); Mockito.when(item.getDiscountForCountry("Israel")).thenReturn(100); Author: Gal Lalouche - Technion 2017 © Mockito.when(item.getDiscountForAmount(any()) .thenAnswer(amount -> amount * 0.1); Can also do this with several mock objects and verify order between them ShoppingSuggestion ssSpy = Mockito.spy(new ShoppingSuggestion());

Mockito: Mocking generic classes Mocking generic class can be annoying due to the type erasure We can use rules and annotation to make our life a bit easier Foo<T> foo = mock(Foo.class); // unchecked warning ArgumentCaptor<Foo<T>> captor = ArgumentCaptor.forClass(Foo.class); // unchecked warning @Mock private Foo<T> foo; @Captor private ArgumentCaptor<Foo<T>> captor; @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); Author: Gal Lalouche - Technion 2017 © Can also do this with several mock objects and verify order between them

Mockito: Verifying order Mockito offers a few gems, such as verifying the order of method invocations @Test public void testInOrder() { ShoppingSuggestions ss = Mockito.mock(ShoppingSuggestions.class); ShoppingCart cart = new ShoppingCart(ss); Item item1 = Mockito.mock(Item.class); Item item2 = Mockito.mock(Item.class); Item item3 = Mockito.mock(Item.class); cart.addItem(item1); cart.addItem(item2); cart.addItem(item3); InOrder inOrder = Mockito.inOrder(ss); inOrder.verify(ss).displaySuggestionFor(item1); inOrder.verify(ss).displaySuggestionFor(item2); inOrder.verify(ss).displaySuggestionFor(item3); Mockito.verifyNoMoreInteractions(ss); } Author: Gal Lalouche - Technion 2017 © Can also do this with several mock objects and verify order between them