Programmer Testing Testing all things Java using JUnit and extensions

  • Published on
    24-Dec-2015

  • View
    214

  • Download
    2

Embed Size (px)

Transcript

<ul><li> Slide 1 </li> <li> Programmer Testing Testing all things Java using JUnit and extensions </li> <li> Slide 2 </li> <li> The Big Why If you dont have tests, how do you know your code is doing the thing right and doing the right thing ? </li> <li> Slide 3 </li> <li> What are Programmer Tests? Programmer Testing is the testing performed by a developer with the goal of verifying the correct functioning of her code Programmer Tests are automated unit tests that exercise program units such as classes and methods, not the complete features or requirements supported by the software as a whole Important distinction: if its not automated, its not really programmer testing thats exploratory testing. </li> <li> Slide 4 </li> <li> Safety Net Having a good test coverage with an automated unit test harness prevents a system from becoming legacy(*), and enables effective refactoring, because it also prevents regression Metaphor: Mold * Michael Feathers defines legacy code as code without tests </li> <li> Slide 5 </li> <li> Available tools for Java JUnit The de facto Java unit testing framework Extremely simple Plenty of extensions for J2EE, for example TestNG Very close to JUnit but not quite Employs @metadata instead of naming conventions </li> <li> Slide 6 </li> <li> JUnit What is the difference between a framework and a library? Is JUnit (www.junit.org) a framework or a library? </li> <li> Slide 7 </li> <li> JUnit 101 Essential concepts: Test suite: Java class that extends junit.framework.TestSuite or implements a static method named suite(), returning a TestSuite Test case: Java class extending junit.framework.TestCase Test fixture: the initial state of a Test Case, consisting of the member variables initialized through a method named setUp() Test method: a method of a TestCase class of which signature looks like public void testFoo(), representing a single logical test </li> <li> Slide 8 </li> <li> TestCase lifecycle setUp() gets called once before each test method is executed tearDown() gets called once after each test method has been executed regardless of whether the test passed or failed (or threw some other exception). Each testSomething() method gets called exactly once in an arbitrary order! The same instance of a TestCase may or may not be used for executing the tests dont depend on the constructor, use setUp() for setup! </li> <li> Slide 9 </li> <li> Java 1.5 annotations JUnit 3.8 Java 1.4 public void setUp() {... } public void testSomething() {... } public void tearDown() {... } JUnit 4.1 Java 1.5 @Before someSetUp() {... } @Test public void something() {... } @After public void someTearDown () {... } </li> <li> Slide 10 </li> <li> What else is new in JUnit 4.1? Parametrized tests - same testcase is repeated for various argument sets. Annotated way to write test suites and to filter out testcases which (not) to execute </li> <li> Slide 11 </li> <li> JUnit 101: Simple TestCase (1) import junit.framework.*; public class TestCalculator extends TestCase { protected void setUp() { super.setUp(); } protected void tearDown() { super.tearDown(); } </li> <li> Slide 12 </li> <li> JUnit 101: Simple TestCase (2) import junit.framework.*; public class TestCalculator extends TestCase { private Calculator calculator; protected void setUp() { super.setUp(); calculator = new Calculator(); } </li> <li> Slide 13 </li> <li> JUnit 101: Simple TestCase (3) import junit.framework.*; public class TestCalculator extends TestCase { private Calculator calculator; public void testAddingPositiveIntegers() { int expected = 5; int actual = calculator.add(2, 3); assertEquals(2 + 3 should be 5, expected, actual); } </li> <li> Slide 14 </li> <li> JUnit 101: Simple TestCase (4) import junit.framework.*; public class TestCalculator extends TestCase { private Calculator calculator; protected void setUp() { } protected void tearDown() { } public void testAddingPositiveIntegers() { } public void testAddingNegativeIntegers() { } public void testAddingZeroes() { } public void testAddingPositiveToNegative() { } } </li> <li> Slide 15 </li> <li> JUnit 101: Simple TestSuite (1) import junit.framework.*; public class CalculatorTestSuite extends TestCase { public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest(CalculatorIntegerTest.class); suite.addTest(CalculatorFloatingPointTest.class); suite.addTest(CalculatorLogarithmsTest.class); return suite; } </li> <li> Slide 16 </li> <li> What if I need to do something expensive in setUp()? If you need to perform some kind of preparations before running the test methods of a TestCase (and cant/wont replace the expensive stuff with a mock implementation), you need to wrap your TestCase with a junit.extensions.TestSetup decorator class. </li> <li> Slide 17 </li> <li> Wrapping TestCase into TestSetup public class InterestCalculatorTestWithTestSetup extends TestCase { private static double interestRate, loanAmount; private static int loanDuration; public static Test suite() { TestSuite suite = new TestSuite(InterestCalculatorTestWithTestSetup.class); TestSetup wrapper = new TestSetup(suite) { public void setUp() throws IOException { ResourceBundle bundle = ResourceBundler.getBundle( InterestCalculatorTestWithTestSetup.class.getName()); inrerestRate = Double.parseDouble(bundle.getString(interest.rate)); loanAmount = Double.parseDouble(bundle.getString(loan.amount)); loanDuration = Interest.parseInt(bundle.getString(loan.duration)); } }; return wrapper; } public void testInterestCalculation() { } } </li> <li> Slide 18 </li> <li> Assertions We already saw one assertion in action, the assertEquals() method There are a bunch others take a look at the Javadocs for junit.framework.TestCase at http://www.junit.org/junit/javadoc/3.8.1/ http://www.junit.org/junit/javadoc/3.8.1/ The most often used assertions assertEquals(), assertEquals(), assertNull(), assertSame() and their opposites are enough for most situations and the rest you can easily write on your own. </li> <li> Slide 19 </li> <li> What happens when an assertion fails? The assertions throw a junit.framework.AssertionFailedError when they fail. JUnits TestRunner catches these (runtime)exceptions and tags the test as failure for later reference. If a test throws any other exception (e.g. NullPointerException), JUnit again catches these but tags the test as error instead of failure. </li> <li> Slide 20 </li> <li> Mock Objects At some point, you will face a problem where the class/method you should be testing needs to collaborate with an object that is either difficult to create/obtain or simply sooo slooow that your test takes forever to execute (even a 100ms adds up when youve got thousands of tests). The solution for this kind of problems is often to use a mock object. </li> <li> Slide 21 </li> <li> What are Mock Objects? A mock object is an object that claims to implement an interface but doesnt really. There are variations on what the doesnt really part actually means (these variations are called fake, stub and mock) but for now, well just call them all mock objects. Since theres nothing like a good example </li> <li> Slide 22 </li> <li> Example: Mock Objects public class Item { public float getPriceAfterDiscounts(PricingService ps, DiscountService ds) { float basePrice = ps.getPriceFor(this); float discountedPrice = ds.applyDiscounts(this, basePrice); return discountedPrice; } public interface PricingService { float getPriceFor(Item item); } public interface DiscountService { float applyDiscounts(Item item, float basePrice); } </li> <li> Slide 23 </li> <li> How to test that without the real PricingService &amp; DiscountService? public class TestItemUsingMockObjects extends TestCase { private Item item; private PricingService pricingService; private DiscountService discountService; protected void setUp() { item = new Item(); pricingService = new PricingService() { public float getPriceFor(Item item) { return 12.34f; } }; discountService = new DiscountService() { public float applyDiscounts(Item item, float basePrice) { } }; } public void testCalculateDiscountsOnBasePrice() { assertEquals(10.95f, item.getPriceAfterDiscounts(pricingService, discountService); } </li> <li> Slide 24 </li> <li> Static vs. Dynamic Mocks That example used static mock objects the other school of mock objects is dynamic mock objects Static mock objects fake their own behavior while dynamic mock objects also verify that the class under test collaborated with the mock objects as expected Lets see an example to illustrate this behavior-oriented approach to using mock objects (well use the EasyMock framework but there are some alternatives) </li> <li> Slide 25 </li> <li> Using EasyMock and dynamic mock objects public class TestItemUsingMockObjects extends TestCase { private Item item; private MockControl pricingControl, discountControl; private PricingService pricingService; private DiscountService discountService; protected void setUp() { item = new Item(); pricingControl = MockControl.createControl(PricingService.class); // obtain a remote control pricingService = (PricingService) pricingControl.getMock(); // let EasyMock create the mock object pricingService.getPriceFor(item); // specify expected method call to our mock PricingService pricingControl.setReturnValue(12.34f); // fake the behavior by setting return value pricingControl.replay(); // switch from recording mode to replay mode // similar setup for DiscountService... } public void testCalculateDiscountsOnBasePrice() { assertEquals(10.95f, item.getPriceAfterDiscounts(pricingService, discountService); pricingControl.verify(); // verify expected interactions actually happened discountControl.verify(); // verify expected interactions actually happened } </li> </ul>

Recommended

View more >