+ All Categories
Home > Documents > Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the...

Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the...

Date post: 14-Oct-2020
Category:
Upload: others
View: 2 times
Download: 0 times
Share this document with a friend
20
Designing For Testability
Transcript
Page 1: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Designing For Testability

Page 2: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Characteristics of Testable Code

• Highly cohesive • Loosely coupled

– Dependencies minimized• Dependency injected• Minimal use of static methods and final methods• Static methods contain few parameters• Complex object creation logic is placed in Factories and/or Builders

Page 3: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Features that are Difficult to Automate

• Dependencies created by the class under test– Prevents mocking / unit isolation

• Final methods– Can’t be overridden by mocks or other types of test doubles

• Static methods– Can’t be overridden by mocks or other types of test doubles

• Code that contains complex object creation logic (especially if it creates complex object graphs)

• GUI / View Code

Page 4: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Strategies

• Test-Driven Development• Dependency Injection• Use of configurable factories with simple (preferably no-argument)

constructors• In-class wrapper methods for calls to static method• Use of MVC / MVP with view interfaces

Page 5: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Problem: Dependencies Created by the Class Under Test

public class PayrollProcessor {private DatabaseService dbService;

public PayrollProcessor() {dbService = new DatabaseService();

}

public double getTaxAmount(String employeeID) {Employee emp = dbService.getEmployee(employeeID);

double taxAmount = 0;

// Some complicated code that uses information from// 'emp' to calculate taxes

return taxAmount;}

}

Page 6: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Solution• Dependency Injection• Mocking of the DatabaseService instance in the test

public class PayrollProcessor2 {private DatabaseService dbService;

public PayrollProcessor2(DatabaseService dbService) {this.dbService = dbService;

}

public double getTaxAmount(String employeeID) {Employee emp = dbService.getEmployee(employeeID);

double taxAmount = 0;

// Some complicated code that uses information from 'emp' to // calculate taxes

return taxAmount;}

}

Page 7: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Solution: Setter Injection

public class PayrollProcessor3 {private DatabaseService dbService;

public void setDbService(DatabaseService dbService) {this.dbService = dbService;

}

public double getTaxAmount(String employeeID) {Employee emp = dbService.getEmployee(employeeID);

double taxAmount = 0;

// Some complicated code that uses information from 'emp' to // calculate taxes

return taxAmount;}

}

Page 8: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

The “Mock Object” design pattern

• Allows “mock objects” to be inserted anywhere in a program

• Allows a class to be isolated from its dependencies during unit testing, or for other reasons (e.g., a class I depend on doesn’t exist yet, or I want to avoid calling it for some reason)

• Supports “fault injection”– Cause the software to fail at points where it normally wouldn’t to

test error handling code

• Easy to generate with frameworks such as Mockito and EasyMock

Page 9: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

• Mock objects can simulate the behavior of complex, real (non-mock) objects and are therefore useful when a real object is impractical or impossible to incorporate into a unit test.

• If an object has any of the following characteristics, it may be useful to use a mock object in its place:– supplies non-deterministic results (e.g., current time or current temperature);– has states that are difficult to create or reproduce (e.g., a network error);– is slow (e.g., a complete database, which would have to be initialized);– does not yet exist or may change behavior;– would have to include information and methods exclusively for testing

purposes (and not for its actual task).• Mock object method implementations can contain assertions of their own. This

means that a true mock, in this sense, will examine the context of each call—perhaps checking the order in which its methods are called, perhaps performing tests on the data passed into the method calls as arguments.

The “Mock Object” design pattern

Page 10: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Mock Object Creation with Mockitopublic class PayrollProcessorTest {

@Testpublic void testGetTaxAmount() {

DatabaseService mockDBService = Mockito.mock(DatabaseService.class);

Employee mockEmployee = Mockito.mock(Employee.class);Mockito.doReturn(mockEmployee).when(

mockDBService.getEmployee(Mockito.anyString()));

// Mockito.when calls to setup mockEmployee to do what we will write // our test to expect

PayrollProcessor2 processor = new PayrollProcessor2(mockDBService);

// Code to test the getTaxAmount() method assuming that the // DatabaseService returns an employee object that does what we mocked // our mockEmployee to do

}}

Page 11: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Dependency Injection• Programming style that allows objects to receive their dependencies

instead of creating them• Makes your design more flexible (dependencies are no longer “hard-

coded” into the class that uses them• Facilitates testing by making it easy for a test to mock a dependency• Choose a “Dependency Injection Container”

– Spring, Ninject, Google Guice, etc.• Configure your dependency injection container with a mapping from

abstract interfaces to concrete classes that implement them• Create objects by asking the dependency injection container for an

implementation of an abstract interface• Allows program code to depend on abstract interfaces, and not on

concrete classes• Allows mock objects to be easily inserted anywhere in the code

Page 12: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Problem: Final Methods

• Mocking frameworks mock interfaces by creating their own implementations

• They mock classes by creating subclasses• Final methods cannot be sub-classed, so most mocking

frameworks cannot mock them• Final methods can be tested, but if the dependency you want to mock

has a final method, you typically cannot replace that method with a mock implementation– Can’t do this:

Mockito.doReturn(xyz).when(mockObject.myFinalMethod())

Page 13: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Solutions

1. Minimize the use of final methods (warning: virtual methods are slower than final methods)

2. Use a mocking framework that can mock final methods (i.e. PowerMock)1. PowerMock is slow2. Generally considered bad practice to use it

Page 14: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Problem: Static Methods

• Similar problems as final methods– Can’t be mocked by most mocking frameworks

• Static methods are testable, but classes that have dependencies on them are difficult to test

Page 15: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Solutions

1. Minimize the use of static methods2. Wrap calls to static methods in non-static methods in the dependent

class3. Use a mocking framework that can mock static methods (i.e.

PowerMock)

Page 16: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Problem: Code that Contains Complex Creation Logic

• Similar to the general problem of classes that create their own dependencies

• Difficult to use dependency injection

Page 17: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Solution

• Use a factory to create the dependency object(s)• Use a non-static ‘get…’ method to create the object

– Makes the entire factory mockable

public class ServiceFactory {public DatabaseService getDatabaseService() {

DatabaseService dbService = null;

// Complex logic to create and initialize a // database service instance.

return dbService;}

}

Page 18: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Factory in Greater Detail

Page 19: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Problem: GUI / View Code

• GUIs are difficult to test using test automation tools• GUIs change frequently (making GUI test code brittle)• GUI automation tools (i.e. Selenium for browser-based applications)

are somewhat flaky• GUI tests that automate and mimic user behavior are slow (due to

screen creation and redraws)

Page 20: Designing For Testabilitycs340ta/winter2019/notes/rodham/18... · • Dependencies created by the class under test –Prevents mocking / unit isolation • Final methods –Can’t

Solution

• Use an MVC / MVP design– Separate actual view code from what can go in controller/presenter

and model classes that are easier to test

• Use interfaces for your views and make controllers/presenters only depend on the view interfaces (not the actual views)– Allows mocking of the view code– Will still need a few system level tests (automated or manual) but

most view code can be tested without actually generating the view


Recommended