towards a testable application architecture

21
Towards a testable Application Architecture Erik Doernenburg John Horgan

Upload: others

Post on 01-Feb-2022

4 views

Category:

Documents


0 download

TRANSCRIPT

Towards a testableApplication Architecture

Erik DoernenburgJohn Horgan

Mock Objects

Dependency Injection

Containers

PicoContainer

Case Study

3© Copyright ThoughtWorks, Inc.® 2004

Collaborating Objects

• An object-oriented program is organised as a web of collaborating objects

• To test an object, we test its interactions with its neighbours

4© Copyright ThoughtWorks, Inc.® 2004

Using Mocks

• To isolate the tested object we replace its neighbours with mock objects

Object under testTest case

Mock

Mock

5© Copyright ThoughtWorks, Inc.® 2004

Mocks and layers

• Minimize uncertainty by testing each layer/component in isolation before integration it

• Rely only on already tested components � manage dependency

BB

CC

AA AA

BB

CC

AA

Mocks

Test fixturesLayers

Mock Objects

Dependency Injection

Containers

PicoContainer

Case Study

7© Copyright ThoughtWorks, Inc.® 2004

Dependency resolution

• Service Locator

• Dependency Injection

Service Locator

Dependency Injector

8© Copyright ThoughtWorks, Inc.® 2004

Show me the code

public class Foo {public Foo() {}public void doSomething() {

MyService service = ServiceLocator.getService();service().doIt();

}

public class Foo {protected MyService service;

public Foo(MyService aService) {service = aService;

}public void doSomething() {

service().doIt();}

Mock Objects

Dependency Injection

Containers

PicoContainer

Case Study

10© Copyright ThoughtWorks, Inc.® 2004

In-container testing

• Containers provide useful infrastructure, but testing a service component inside a container adds complexity.

Test case

EJB

J2EE Container

11© Copyright ThoughtWorks, Inc.® 2004

Technology wrappers

• Separating application logic and infrastructure code makes testing of the service component easier.

Test case

Servicecomponent

EJB

J2EE Container

Servicecomponent

12© Copyright ThoughtWorks, Inc.® 2004

Choosing the infrastructure

• Can access dependent components with best interface.

EJB

J2EE Container

DealManager

MarketData

DealDistribution

• Can add other technology wrappers.

WebService

Mock Objects

Dependency Injection

Containers

PicoContainer

Case Study

14© Copyright ThoughtWorks, Inc.® 2004

Component dependencies

• How do these relationships get established?

? ??

??

??

?

? ??

15© Copyright ThoughtWorks, Inc.® 2004

PicoContainer

���� ���������

• A simple Constructor Injection container

classes objects

16© Copyright ThoughtWorks, Inc.® 2004

Show me the code

public class DealManagerServiceImpl {

public DealManagerImpl(MarketData aMarketDataService) {

}

public class DealEntryMessageBean {

public void doSomething() {

PicoContainer pico = getPicoContainer();

pico.getComponentInstance(DealManager.class).doIt();

}

public PicoContainer getPicoContainer() {

MutablePicoContainer pico = new DefaultPicoContainer();

pico.registerComponentImplementation(DealManagerServiceImpl.class);

pico.registerComponentImplementation(MarketDataServiceImpl.class);

return pico;

}

Mock Objects

Dependency Injection

Containers

PicoContainer

Case Study

18© Copyright ThoughtWorks, Inc.® 2004

Refactoring for testability

1. From EJBs with embedded logic and service locatorstowardsan EJB wrapper using PicoContainer and service objects.

2. From Struts Actions with Service LocatorstowardsStruts Actions with Dependency Injection.

19© Copyright ThoughtWorks, Inc.® 2004

[Switch to development environment]

20© Copyright ThoughtWorks, Inc.® 2004

CustomActionProcessor.javapackage com.thoughtworks.dealmanager.util;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;

import org.apache.struts.action.ActionMapping;

import org.apache.struts.action.RequestProcessor;

import org.apache.struts.util.RequestUtils;

import org.picocontainer.MutablePicoContainer;

import org.picocontainer.defaults.DefaultPicoContainer;

import com.thoughtworks.dealmanager.services.DealDistributionService;

import com.thoughtworks.dealmanager.services.DealEntryService;

public class CustomRequestProcessor

extends RequestProcessor

{

public Action processActionCreate(HttpServletRequest

request,

HttpServletResponse

response, ActionMapping

mapping)

throws IOException

{

Action action

= null;

try

{

String className

= mapping.getType();

MutablePicoContainer

pico

= getPicoContainer();

Class actionClass

= RequestUtils.applicationClass(className);

pico.registerComponentImplementation(actionClass);

action = (Action)pico.getComponentInstance(actionClass);

action.setServlet(servlet);

} catch(Exception

e)

{

response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR

getInternal().getMessage("actionCreate", mapping.getPath()));

} return action;

} private MutablePicoContainer

getPicoContainer()

{

MutablePicoContainer

pico

= new DefaultPicoContainer();

pico.registerComponentImplementation(DealDistributionService.class);

pico.registerComponentImplementation(DealEntryService.class);

return pico;

}

}

Thank you

[email protected]