Share
Embed
Transcript
35 slides
-
Agenda • Overview • Mocks & Expects • Replaying •
Verifying • Advanced Expects • Partial Mocks • Strict Mocks • Captures • PowerMock • General Tips Thursday, August 29, 13 -
Overview • Java test library • Groovy works with some
effort • Integrates into JUnit test cases • Test driven development • Achieve high code/branch coverage • Cobertura and other plugins Thursday, August 29, 13 -
General Outline @Test public testMyMock() { // Create mocks //
Establish expectations // Replay mocks EasyMock.replay(mock1, mock2, ...); // Execute test case // Verify mocks EasyMock.verify(mock1, mock2, ...); } Thursday, August 29, 13 -
Mocks • Easily abstracted, wrapped Object • Define specific behaviors,
for certain scenarios on the Mocked Object • Validate the expected behaviors • Enables granular testing • Can achieve 100% source & branch coverage Thursday, August 29, 13 -
Mocks Example SimpleClass simple; @Before public void setup() { //
Useful for commonly used mocks. // Reset between tests. simple = EasyMock.createMock(SimpleClass.class); } @Test public void testMethod() { // Mock specific to this test only Complex complex = EasyMock.createMock(Complex.class); ... } Thursday, August 29, 13 -
Expectations • Winding up the Mocks • Establish return values
• Throw exceptions • Check/capture method params • Void method expectations • Chain expectations together - Caution! Thursday, August 29, 13 -
Expectations Example import static org.easymock.EasyMock.*; @Test public void testPerform() {
Complex complex = createMock(Complex.class); // Simple expect - perform() returns a String expect(complex.perform()).andReturn(“that was easy”); // Simple throw exception expect(complex.perform()).andThrow(new WTFException()); // Ordered chain - 3 expectations expect(complex.perform()) .andReturn(“hard”) .andReturn(“easy”) .andThrow(new WTFException()); ... } Thursday, August 29, 13 -
Expectations Example cont. import static org.easymock.EasyMock.*; @Test public void testCompute()
{ Complex complex = createMock(Complex.class); // 1) Simple expect - setup() is a void method complex.setup(); expectLastCall(); // Optional for a single call // 2) Back-to-back setup calls complex.setup(); complex.setup(); complex.setup(); // 3) Simplified version of 2) - they are equivalent complex.setup(); expectLastCall().times(3); } Thursday, August 29, 13 -
Replay • Post expectations, ‘replay’ each mock • Preps mock
object for test execution • Required to use mock object • Will get NPE, otherwise Thursday, August 29, 13 -
Verify • Verify that all expectations were met • Optional,
though should be required!! • Without, not all expectations have to be met for tests to pass • Inadvertently miss intended expects • Between each @Test mocks are reset Thursday, August 29, 13 -
Advanced Expectations • How about checking params passed into the
mocked methods? • Specify a .equals() equivalent value • Matchers • aryEq(someArray), isA(xxx.class) • eq(xxx), anyPrimitive() - (int, long, boolean) “If you would like to use matchers in a call, you have to specify matchers for all arguments of the method call.” Thursday, August 29, 13 -
Advanced Expectations Ex. import static org.easymock.EasyMock.*; @Test public void testCompute()
{ Adder adder = createMock(Adder.class); // 1) Specific values expect expect(adder.add(1, 2)).andReturn(3); // 2) Any type of int - boundary condition of -1 expect(adder.add(anyInt(), anyInt()).andReturn(-1); // 3) Class-wise generic - again, boundary condition of -1 expect(adder.add(isA(Integer.class), eq(new Integer(4)) .andReturn(-1); } Thursday, August 29, 13 -
Internal Methods class Complex { private boolean isSetup = false;
public String perform() { if (!this.isSetup) { isSetup = this.setup(); } // Seriously hard computations ... } protected boolean setup() { // Lengthy setup involving numerous external classes ... return true; } } • How about mocking/stubbing internal methods? Thursday, August 29, 13 -
Internal Methods cont. class Complex { private boolean isSetup =
false; public String perform() { if (!this.isSetup) { isSetup = this.setup(); } // Seriously hard computations ... } protected boolean setup() { // Lengthy setup involving numerous external classes ... return true; } } @Test public void testMethod() { // Mock out the setup method in perform(); Complex complex = new Complex() { @Override protected boolean setup() { return true; } }; complex.perform(); // Note that we test on our partial mock } Right?? • How about mocking internal methods? Thursday, August 29, 13 -
Internal Methods cont. • How about mocking internal methods? class
Complex { private boolean isSetup = false; public String perform() { if (!this.isSetup) { isSetup = this.setup(); } // Seriously hard computations ... } protected boolean setup() { // Lengthy setup involving numerous external classes ... return true; } } @Test public void testMethod() { // Mock out the setup method in perform(); Complex complex = new Complex() { @Override protected boolean setup() { return true; } }; complex.perform(); // Note that we test on our partial mock } Thursday, August 29, 13 -
What’s Wrong?? • Hard to maintain test as code evolves
• Too tightly coupled with testing class • Forces scope to be protected/public • Partial Mocks are less code too Thursday, August 29, 13 -
Partial Mocks • Mock internal methods of test class •
Inherit from testing class • @Override methods • Highly recommended for Groovy • Meta classing issues • Can specify method/constructor params, etc. Thursday, August 29, 13 -
Partial Mocks Example class Complex() { private boolean isSetup =
false; public String perform() { if (!this.isSetup) { isSetup = this.setup(); } // Seriously hard computations ... } protected boolean setup() { // Lengthy setup involving numerous external classes ... return true; } } @Test public void testMethod() { // Mock out the setup method in perform(); Complex complex = EasyMock.createMockBuilder(Complex.class) .addMockedMethod(“setup”).createMock(); expect(complex.setup()).andReturn(true); replay(complex); complex.perform(); // Note that we test on our partial mock verify(complex); } Thursday, August 29, 13 -
Strict Mocks • Advanced mock type (not typical) • Order
checking on mocks • Fails test if operation sequence deviates • Useful for very specific test types Thursday, August 29, 13 -
Strict Mocks Example protected final static String INSERT = "INSERT
INTO searchtarget " + ST_VALUES + " VALUES (?, ?, ?, ?);"; protected void buildQuery(String query, List values) { PreparedCqlQuery<String, String> statement = searchTargetKeyspace.prepareQuery(ST_RESULTS_CF) .withCql(cqlQuery).asPreparedStatement(); for (Object o : values) { if (o instanceof String) { statement.withStringValue((String)o); } else if (o instanceof Integer) { statement.withIntegerValue((Integer)o); } else { statement.withLongValue((Long)o); } } } @Test public void testInsertQuery() { CqlQuery cqlQuery = createMock(CqlQuery.class); PreparedCqlQuery query = createStrictMock(PreparedCqlQuery.class); String queryStr = “testQuery”; expect(keyspace.prepareQuery(isA(ColumnFamily.class))).andReturn(cfQuery); expect(cfQuery.withCql(myQuery)).andReturn(cqlQuery); expect(cqlQuery.asPreparedStatement()).andReturn(query); expect(query.withStringValue("valueFirst")).andReturn(query); expect(query.withIntegerValue(123)).andReturn(query); expect(query.withStringValue("foursquare")).andReturn(query); expect(query.withLongValue(456L)).andReturn(query); EasyMock.replay(cqlQuery, query); new MyTestClass().buildQuery(queryStr, Lists.newArrayList(“valueFirst”, 123, “foursquare”, 456L); EasyMock.verify(cqlQuery, query); } Thursday, August 29, 13 -
Observing Method Variables • How about checking the actual param
values passed into the mocked methods? @Test public void testMethod() { // Mock out the setup method in perform(); Connection connection = new Connection() { @Override protected boolean sendResult(Map result) { Assert.assertEquals(5, result.size()); Assert.assertEquals(“Michie”, results.get(“Ken”); ... return true; } }; new MyTest().save(connection); // invokes sendResult() } Right?? Thursday, August 29, 13 -
• How about checking param values passed into the mocked
methods? @Test public void testMethod() { // Mock out the setup method in perform(); Connection connection = new Connection() { @Override protected boolean sendResult(Map result) { Assert.assertEquals(5, result.size()); Assert.assertEquals(“Michie”, results.get(“Ken”); ... return true; } }; new MyTest().save(connection); // invokes sendResult() } Observing Method Variables Thursday, August 29, 13 -
Captures • Mocked call passes a complex Object • Verbose
constructor, large Map, etc. • Provides better checking than isA(xxx.class), anyXXX() ... • Don’t have to @Override a method call to grab its value (Remember Partial Mocks ex?) Thursday, August 29, 13 -
Capture Examples import static org.easymock.EasyMock.*; @Test public void testCapture() {
Connection connection = createMock(Connection.class); // Capturing a Map with many values, too hard to construct Capture<Map<String, String>> myCapture = new Capture<Map<String, String>>(); // Mocks method ‘boolean exchangeState(String, Map); expect(connection.sendResult(EasyMock.capture(myCapture)).andReturn(true); replay(complex); new MyTest().save(connection); // Validate contents of the capture Map<String, String> capturedMap = capture.getValue() Assert.assertEquals(5, capturedMap.size()); Assert.assertEquals(“Michie”, capturedMap.get(“Ken”); ... verify(complex); } Thursday, August 29, 13 -
Toughies for EasyMock • What about those pesky static methods?
class DBAggregator() { protected static isJUnit = false; private static DBAggregator myInstance = null; private static List<DBConnections> connections = null; public static synchronized getInstance() { if (myInstance == null) { myInstance = new DBAggregator(); if (!isJUnit) { connections = Connections.setupStaticDBConnections(); } } return myInstance; } } Right?? Thursday, August 29, 13 -
Toughies for EasyMock • What about those pesky static methods?
class DBAggregator() { protected static isJUnit = false; private static DBAggregator myInstance = null; private static List<DBConnections> connections = null; public static synchronized getInstance() { if (myInstance == null) { myInstance = new DBAggregator(); if (!isJUnit) { connections = Connections.setupStaticDBConnections(); } } return myInstance; } } Thursday, August 29, 13 -
PowerMock • Solution to those tricky ones in EasyMock •
Static methods/initializers • Constructors • Final classes and methods • Private methods • PLUS it integrates seamlessly into EasyMock! Thursday, August 29, 13 -
PowerMock Example class DBAggregator() { private static DBAggregator myInstance =
null; private static List<DBConnections> connections = null; public static synchronized getInstance() { if (myInstance == null) { myInstance = new DBAggregator(); connections = Connections.setupStaticDBConnections(); } return myInstance; } } @RunWith(PowerMockRunner.class) @PrepareForTest(Connections.class) public class DBAggregatorTest { @Test public void testGetInstance() { List<DBConnections> conns = new ArrayList<DBConnections>(); mockStatic(Connections.class); expect(Connections.setupStaticDBConnections).andReturn(conns); PowerMock.replay(Connections.class); DBAggregator.getInstance(); PowerMock.verify(Connections.class); } } Thursday, August 29, 13 -
Tips and Tricks • Spend just 1 extra hour writing
tests • Saves you time later! Promise! • Consolidate common setup methods • @Before is a nice place for Mocks/excepts • You can reset mocks in EasyMock Thursday, August 29, 13 -
Tips and Tricks public class MyTest { public void replayAll(Object
... objs) { for (Object o : objs) { EasyMock.replay(o); } EasyMock.replay(commonMock1, ...); PowerMock.replayAll(); } public void verifyAll(Object ... objs) { for (Object o : objs) { EasyMock.verify(o); } EasyMock.verify(commonMock1, ...); PowerMock.verifyAll(); } ... } • PowerMock conveniently already has replayAll and verifyAll(Object... objs) Thursday, August 29, 13 -
Links, etc. • EasyMock & PowerMock • http://easymock.org/ EasyMock3_1_Documentation.html •
https://code.google.com/p/powermock/ • Gradle testCompile 'junit:junit:4.11' testCompile 'org.easymock:easymock:3.1' testCompile 'org.powermock:powermock-easymock-release-full:1.4.10' Thursday, August 29, 13