In the previous post, I introduced basic of Unit Testing.
Next I introduce parameterized testing.
Before explaning parameterized testing, let's define a simple service class which uses ExchangeRateApi calss.
If you would like to test this PriceCalculator class by a bunch of currency combination, you can write test for each currency combination like...
In order to reduce this kind of duplication, we can use parameterized test.
You can write the parameterized test for above case by using @Parameters annotation.
Medthod annotated by @Parametrized should return parametrs formatted as Collection<Object[]&. Each Object[] indicates each test entry.
Please see below code.
(And I think JUnit team is working for this problem.)
Next I introduce parameterized testing.
Before explaning parameterized testing, let's define a simple service class which uses ExchangeRateApi calss.
package com.dukesoftware.exchangerate.service; import java.util.HashMap; import java.util.Map; import com.dukesoftware.exchangerate.api.ExchangeRateApi; import com.dukesoftware.exchangerate.api.Rate; public class PriceCalculator { private final ExchangeRateApi api; private final Map<String, Rate> map = new HashMap<>(); private final static double FEE_RATE = 0.05; public PriceCalculator(ExchangeRateApi api) { this.api = api; } public void initialize() { } public void shutdown() { this.map.clear(); } public double calculatePrice(double price, String ccy1, String ccy2) { Rate rate = this.map.get(ccy1+":"+ccy2); if(rate == null) { // caching rate = this.api.getRate(ccy1, ccy2); this.map.put(ccy1+":"+ccy2, rate); } return rate.getValue() * (price * (1 + FEE_RATE)); } }
If you would like to test this PriceCalculator class by a bunch of currency combination, you can write test for each currency combination like...
package com.dukesoftware.exchangerate.service; import junit.framework.Assert; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.dukesoftware.exchangerate.api.YahooExchangeRateApi; import com.dukesoftware.exchangerate.service.PriceCalculator; // TODO should use "Parameterized Test" public class PriceCalculatorNotUsingParameterizedTest { private PriceCalculator service; @Before public void setUp() { // inject this.service = new PriceCalculator(new YahooExchangeRateApi()); this.service.initialize(); } @After public void tearDown() { this.service.shutdown(); } @Test public void testCalculateJPYtoUSD() { double priceUSD = this.service.calculatePrice(10000, "JPY", "USD"); Assert.assertEquals(100, priceUSD, 0.0001); } @Test public void testCalculatePHPtoUSD() { double priceUSD = this.service.calculatePrice(10000, "PHP", "USD"); Assert.assertEquals(100, priceUSD, 0.0001); } @Test public void testCalculateHKDtoUSD() { double priceUSD = this.service.calculatePrice(10000, "HKD", "USD"); Assert.assertEquals(100, priceUSD, 0.0001); } @Test public void testCalculateCHFtoUSD() { double priceUSD = this.service.calculatePrice(10000, "CHF", "USD"); Assert.assertEquals(100, priceUSD, 0.0001); } }This is fine but, as you can see, a lot of part is duplicated.
In order to reduce this kind of duplication, we can use parameterized test.
You can write the parameterized test for above case by using @Parameters annotation.
Medthod annotated by @Parametrized should return parametrs formatted as Collection<Object[]&. Each Object[] indicates each test entry.
Please see below code.
package com.dukesoftware.exchangerate.service; import java.util.Arrays; import java.util.Collection; import junit.framework.Assert; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import com.dukesoftware.exchangerate.api.YahooExchangeRateApi; import com.dukesoftware.exchangerate.service.PriceCalculator; @RunWith(Parameterized.class) public class PriceCalculatorParameterizedTest { // write parameters here... @Parameters public static Collection<Object[]> parameters() { return Arrays.asList( new Object[][]{ { 100, 1.0395, "JPY", "USD"}, { 100, 2.4045, "PHP", "USD"}, { 10000, 1.05, "IDR", "USD"} }); } private PriceCalculator service; private final double price; private final double expectedPrice; private final String ccy1; private final String ccy2; public PriceCalculatorParameterizedTest(double price, double expectedPrice, String ccy1, String ccy2) { this.price = price; this.expectedPrice = expectedPrice; this.ccy1 = ccy1; this.ccy2 = ccy2; } @Before public void setUp() { // inject this.service = new PriceCalculator(new YahooExchangeRateApi()); this.service.initialize(); } @After public void tearDown() { this.service.shutdown(); } @Test public void testCalculatePrice() { double actualPrice = this.service.calculatePrice(price, ccy1, ccy2); Assert.assertEquals(expectedPrice, actualPrice, 0.00001); } }After applying parameterized test, code readability is up, dupliaction is reduced, and Easy to add new combination of parameters :) Only disadvantedge of parameterized test is that each test doesn't have human readable name (still in JUnit4.8). I have seen someone enhance the JUnit library and make each test be able to have name.
(And I think JUnit team is working for this problem.)
コメント