class: center, middle # Dependency Injection, Mock Objects and Automated Testing ## with Java and Mockito --- # Unit vs Integration Tests * A unit test isolates a software component and verifies its correctness. * An integration test verifies the interaction between software components. -- * YMMV, but behavior that only addresses a single software component isn't very interesting. Dependency Injection and Mock Objects let us blur the line between unit and integration tests. --- # What's the issue with Integration Tests * Leads to Finger Pointing > My code is correct, someone must have changed the component that I'm interacting with * Adds External Dependencies * Slower - slow tests are run less frequently * Needs to be installed on local machine or cannot be run offline * Often need fake service to support all test cases (it's hard to get a real service to throw an exception only when you're testing exception handling). --- # What is Dependency Injection? Dependency injection is a software design pattern that implements inversion of control and allows a program design to follow the dependency inversion principle. The term was coined by Martin Fowler. An injection is the passing of a dependency (a service) to a dependent object (a client). The service is made part of the client's state. ___Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.___ ** – Wikipedia** --- # An Example Suppose you are implementing a Web Service and you have the following requirements: 1. Each service request requires username/password authentication. 2. The username/password pair will be authenticated by posting the values in a json object to another service and parsing the response. -- Your Object could: 1. Accept a String Object that identifies the service url, or 2. Accept a Url object that identifies the service, or 3. Accept a Connection Object open to the service, or 4. Accept an Authentication Object that connects to the service, posts a request and parses the response. --- # Decisions, Decisions, Decisions String object vs Url object vs Connection object vs Authentication object * String isn't helpful for testing purposes * Url leads to complex mocks (mocks returning mocks) * Connection is bad design (not thread safe and invoking classes shouldn't need to know how to make a Connection) * Authentication introduces another level of abstraction and **Y**ou **A**ren't **G**oing to **N**eed **I**t. --- # That's why you get the big bucks **YAGNI** might lead you to believe that adding an Authentication Object is adding flexibility you don't need. But adding the Authentication Object can simplify testing by having three true outcomes between your new class and the Authentication Object 1. Credentials are valid 2. Credentials are invalid 3. Something happened that prevents me from saying (Exception) And you can test away on the Authentication object over: 1. Response codes 2. Response content-type 3. Ability to parse response 4. Exceptions --- # Enter Mock Objects Mock objects are simulated objects that mimic the behavior of real objects in controlled ways. Mock objects stand in for real ones, allowing programmers to write unit tests addressing the interaction of the class being tested with the objects being mocked. Ideally, using a tool for easy definition of prescribed behavior. Something like mockito [mockito.github.io](http://mockito.github.io/) --- # Mockito Examples ```java Authenticator mockAuth = Mockito.mock(AuthenticatorJson.class); Mockito.when(mockAuth.authenticate(anyString(),anyString())) .thenReturn(goodResponse); Service.setAuthenticator(mockAuth); // Execute the service and verify behavior is consistent with valid credentials ``` -- ```java Authenticator mockAuth = Mockito.mock(AuthenticatorJson.class); Mockito.when(mockAuth.authenticate(anyString(),anyString())) .thenReturn(badResponse); Service.setAuthenticator(mockAuth); // Execute the service and verify behavior is consistent with invalid credentials ``` -- ```java Authenticator mockAuth = Mockito.mock(AuthenticatorJson.class); AuthenticatorException mockExcept = Mockito.mock(AuthenticatorException.class); Mockito.when(mockAuth.authenticate(anyString(),anyString())) .thenThrow(mockExcept); Service.setAuthenticator(mockAuth); // Execute the service and verify behavior is consistent with authentication exception ``` --- # Know your Limitations * Tests using Mocks are only as good as your understanding of how the mocked objects behave. -- * → You still need integration tests -- * Test against your requirements, not your implementation