$30 off During Our Annual Pro Sale. View Details »

JUnitで闘うレガシーコード改善

YutaSaito
June 19, 2022
710

 JUnitで闘うレガシーコード改善

JJUG CCC 2022 Springで発表した内容です。

YutaSaito

June 19, 2022
Tweet

Transcript

  1. JUnitͰಆ͏ϨΨγʔίʔυվળ
    ++6($$$4QSJOH
    ᜊ౻༔ଠ!:VUB4BJUP

    View Slide

  2. ࣗݾ঺հ
    w ᜊ౻༔ଠ
    w αʔόαΠυΤϯδχΞ؀ڥ੔උͱ͔΍ͬͯΔ
    w ϨΨγʔίʔυͱಆ͖ͬͯ·ͨ͠ɻ ݱࡏ΋

    w 5XJUUFS!:VUB4BJUP

    View Slide

  3. ηογϣϯ಺༰
    w ಺༰
    w +6OJUΛར༻ͯ͠ϨΨγʔίʔυΛվળ͢ΔͨΊʹࢲ͕औΓ૊Μ
    Ͱ͍Δ͜ͱΛ͝঺հ͠·͢ɻ
    w ໨ඪ
    w ೔ʑͷۀ຿ͰϨΨγʔίʔυʹۤ࿑͍ͯ͠Δਓʹɺ+6OJUΛར༻
    ͨ͠ϦϑΝΫλϦϯάΛ஌ͬͯ΋Β͏
    w ٕज़ෛ࠴Λ૿΍͞ͳ͍ͨΊʹςετίʔυΛνʔϜͷจԽʹऔΓ
    ೖΕ͍ͨͱࢥͬͯ΋Β͏

    View Slide

  4. ϨΨγʔίʔυͷྫ
    ࢲ͸͍͔ͭ͘ͷγεςϜͰҎԼͷΑ͏ͳίʔυΛݟΔ͜ͱ͕
    ͋Γ·ͨ͠ɻ
    /**
    * ൢചརӹܭࢉ
    *
    * @param price ঎඼୅ۚ
    * @param shippingSize ঎඼αΠζ
    * @return ച্
    */
    public Sales profitCalculate(int price, ShippingSize shippingSize) {
    if (price < 1_000) {
    throw new IllegalArgumentException("priceʹ͸1,000ԁҎ্Λઃఆ͍ͯͩ͘͠͞ɻ");
    }
    Sales sales = new Sales();
    sales.price = price;
    sales.profit = price;
    // ख਺ྉܭࢉ
    feeCalculate(sales);
    // ഑ૹྉܭࢉ
    deliveryFeeCalculate(sales, shippingSize);
    return sales;
    }

    View Slide

  5. ϨΨγʔίʔυͷྫ
    ࢲ͸͍͔ͭ͘ͷγεςϜͰҎԼͷΑ͏ͳίʔυΛݟΔ͜ͱ͕
    ͋Γ·ͨ͠ɻ
    /**
    * ൢചརӹܭࢉ
    *
    * @param price ঎඼୅ۚ
    * @param shippingSize ঎඼αΠζ
    * @return ച্
    */
    public Sales profitCalculate(int price, ShippingSize shippingSize) {
    if (price < 1_000) {
    throw new IllegalArgumentException("priceʹ͸1,000ԁҎ্Λઃఆ͍ͯͩ͘͠͞ɻ");
    }
    Sales sales = new Sales();
    sales.price = price;
    sales.profit = price;
    // ख਺ྉܭࢉ
    feeCalculate(sales);
    // ഑ૹྉܭࢉ
    deliveryFeeCalculate(sales, shippingSize);
    return sales;
    }
    ϝιουʹTBMFTΛ౉ͯ͠
    ͜ͷϝιουͷதͰTBMFTΛߋ৽͍ͯ͠Δ

    View Slide

  6. ϨΨγʔίʔυʹ৽͍͠ػೳΛ௥Ճ͢Δ
    Ͱ͸͜ͷίʔυʹػೳΛ௥Ճͯ͠Έ·͢ɻ
    ࠓ͚ͩʮख਺ྉແྉʯΩϟϯϖʔϯΛߦ͏ͱ͠·͢ɻ
    /**
    * ൢചརӹܭࢉ
    *
    * @param price ঎඼୅ۚ
    * @param shippingSize ঎඼αΠζ
    * @return ച্
    */
    public Sales profitCalculate(int price, ShippingSize shippingSize) {
    if (price < 1_000) {
    throw new IllegalArgumentException("priceʹ͸1,000ԁҎ্Λઃఆ͍ͯͩ͘͠͞ɻ");
    }
    Sales sales = new Sales();
    sales.price = price;
    sales.profit = price;
    // ख਺ྉܭࢉ
    feeCalculate(sales);
    // ഑ૹྉܭࢉ
    deliveryFeeCalculate(sales, shippingSize);
    return sales;
    }

    View Slide

  7. ৽͍͠ػೳΛ௥Ճ͢Δ
    ख਺ྉΛແྉʹ͢Δ͸͜͜Λ௚ͤ͹ྑ͍ͬΆ͍
    /**
    * ൢചརӹܭࢉ
    *
    * @param price ঎඼୅ۚ
    * @param shippingSize ঎඼αΠζ
    * @return ച্
    */
    public Sales profitCalculate(int price, ShippingSize shippingSize) {
    if (price < 1_000) {
    throw new IllegalArgumentException("priceʹ͸1,000ԁҎ্Λઃఆ͍ͯͩ͘͠͞ɻ");
    }
    Sales sales = new Sales();
    sales.price = price;
    sales.profit = price;
    // ख਺ྉܭࢉ
    feeCalculate(sales);
    // ഑ૹྉܭࢉ
    deliveryFeeCalculate(sales, shippingSize);
    return sales;
    }

    View Slide

  8. ৽͍͠ػೳΛ௥Ճ͢Δ
    ख਺ྉܭࢉΛແྉʹ͢Δίʔυʹมߋ
    private void feeCalculate(Sales sales) {
    int rate;
    if (sales.price < 5_000) {
    rate = 20;
    } else if (sales.price < 10_000) {
    rate = 10;
    } else {
    rate = 5;
    }
    int fee = sales.price * rate / 100;
    sales.fee = fee;
    sales.profit = sales.profit - fee;
    }
    private void feeCalculate(Sales sales) {
    sales.fee = 0;
    }
    ݩͷίʔυ
    मਖ਼ޙͷίʔυ

    View Slide

  9. ৽͍͠ػೳΛ௥Ճ͢Δ
    ػೳվमͰ͖ͨʂ0,ʂ

    View Slide

  10. ৽͍͠ػೳΛ௥Ճ͢Δ
    ػೳվमͰ͖ͨʂ0,ʂ
    Μʁ
    ൢചརӹ͕૝ఆΑΓଟ͍ʁ

    View Slide

  11. όάͷݪҼΛ֬ೝ͢Δ
    શ෦ͷίʔυΛݟͯΈ·͢
    private void feeCalculate(Sales sales) {
    sales.fee = 0;
    }
    private void deliveryFeeCalculate(Sales sales, ShippingSize shippingSize) {
    int deliveryFee = switch (shippingSize) {
    case SMALL -> 100;
    case MEDIUM -> 200;
    case LARGE -> 300;
    };
    // ഑ૹྉ͕300ԁҎ্ͳΒख਺ྉ͔Β100ԁׂҾ
    if (deliveryFee >= 300) {
    sales.fee -= 100;
    sales.profit += 100;
    }
    sales.deliveryFee = deliveryFee;
    sales.profit = sales.profit - deliveryFee;
    }
    // ख਺ྉܭࢉ
    feeCalculate(sales);
    // ഑ૹྉܭࢉ
    deliveryFeeCalculate(sales, shippingSize);
    return sales;

    View Slide

  12. όάͷݪҼΛ֬ೝ͢Δ
    શ෦ͷίʔυΛݟͯΈ·͢
    private void feeCalculate(Sales sales) {
    sales.fee = 0;
    }
    private void deliveryFeeCalculate(Sales sales, ShippingSize shippingSize) {
    int deliveryFee = switch (shippingSize) {
    case SMALL -> 100;
    case MEDIUM -> 200;
    case LARGE -> 300;
    };
    // ഑ૹྉ͕300ԁҎ্ͳΒख਺ྉ͔Β100ԁׂҾ
    if (deliveryFee >= 300) {
    sales.fee -= 100;
    sales.profit += 100;
    }
    sales.deliveryFee = deliveryFee;
    sales.profit = sales.profit - deliveryFee;
    }
    // ख਺ྉܭࢉ
    feeCalculate(sales);
    // ഑ૹྉܭࢉ
    deliveryFeeCalculate(sales, shippingSize);
    return sales;
    ख਺ྉ͕ϚΠφεʹͳΔ

    View Slide

  13. ϨΨγʔίʔυͱ͸
    ࠓճͷίʔυʹ͸ҎԼͷΑ͏ͳ໰୊͕͋Γ·ͨ͠ɻ
    ɾίʔυΛमਖ਼͢ΔͱଞͷՕॴʹ·ͰӨڹͯ͠͠·͏
    ɾίʔυͷཧղʹ͕͔͔࣌ؒΔ Մಡੑ͕௿͍

    ࢲ͕ؔΘ͖ͬͯͨγεςϜͰ͸ɺ͜ͷΑ͏ͳίʔυ͕௕͍ظ
    ؒଘࡏ͠ɺվળ͞Εͣʹܧଓͯ͠ར༻͞Ε͍ͯ·ͨ͠ɻ
    ͜ͷΑ͏ͳঢ়ଶΛϨΨγʔίʔυͱࠓճ͸ݺͼ·͢ɻ

    View Slide

  14. ͦ͏ͩϦϑΝΫλϦϯά͠Α͏ʂ

    View Slide

  15. ͲͷΑ͏ʹमਖ਼͢Δʁ
    ෭࡞༻Λͳͯ͘͠ݺͼग़͠ݩϝιου ౰ϝιου
    ͰΫϥεͷ஋Λઃఆ͞
    ͍ͤͨͰ͢ɻҎԼ͸मਖ਼ޙͷΠϝʔδ
    /**
    * ൢചརӹܭࢉ
    *
    * @param price ঎඼୅ۚ
    * @param shippingSize ঎඼αΠζ
    * @return ച্
    */
    public Sales profitCalculate(int price, ShippingSize shippingSize) {
    if (price < 1_000) {
    throw new IllegalArgumentException("priceʹ͸1,000ԁҎ্Λઃఆ͍ͯͩ͘͠͞ɻ");
    }
    Sales sales = new Sales();
    sales.price = price;
    // ख਺ྉܭࢉ
    int fee = feeCalculate(sales.price);
    // ഑ૹྉܭࢉ
    int deliveryFee = deliveryFeeCalculate(shippingSize);
    // ഑ૹྉ͕300ԁҎ্ͳΒख਺ྉ͔Β100ԁׂҾ
    if (deliveryFee >= 300) {
    fee = max(fee - 100, 100);
    }
    sales.fee = fee;
    sales.deliveryFee = deliveryFee;
    sales.profit = price - fee - deliveryFee;
    return sales;
    }

    View Slide

  16. खಈͰͷςετ͸೉͍͠৔߹΋͋Δ
    ࠓճͷΑ͏ͳίʔυΛϦϑΝΫλϦϯάͯ͠΋ɺ
    σʔλͷ४උ͕େมɺܦ࿏͕ෳࡶͳͲͷཧ༝ͰखಈͰςετ
    ͢Δͷ΋೉͍͠৔߹΋͋Γɺόά͕ൃੜ͢ΔՄೳੑ͕͋Γ·
    ͢ɻ

    View Slide

  17. खಈͰͷςετ͸೉͍͠৔߹΋͋Δ
    ࠓճͷΑ͏ͳίʔυΛϦϑΝΫλϦϯάͯ͠΋ɺ
    σʔλͷ४උ͕େมɺܦ࿏͕ෳࡶͳͲͷཧ༝ͰखಈͰςετ
    ͢Δͷ΋೉͍͠৔߹΋͋Γɺόά͕ൃੜ͢ΔՄೳੑ͕͋Γ·
    ͢ɻ
    +6OJUͰςετίʔυΛॻ͚͹
    ҆શʹϦϑΝΫλϦϯάͰ͖·͢

    View Slide

  18. ςετίʔυͷ࡞੒
    ϦϑΝΫλϦϯάΛߦ͏લʹɺςετίʔυΛ࡞੒͠શͯ
    QBTT͢Δ͜ͱΛ֬ೝ͠·͢ɻ
    @Test
    void ഑ૹྉ͕300ԁ() {
    Sales actual = new SalesService().profitCalculate(1_000, ShippingSize.LARGE);
    assertEquals(1_000, actual.price);
    assertEquals(100, actual.fee);
    assertEquals(300, actual.deliveryFee);
    assertEquals(600, actual.profit);
    }

    View Slide

  19. ϦϑΝΫλϦϯά͢Δ
    Ͱ͸ϦϑΝΫλϦϯά͠·͢ɻ
    /**
    * ൢചརӹܭࢉ
    *
    * @param price ঎඼୅ۚ
    * @param shippingSize ঎඼αΠζ
    * @return ച্
    */
    public Sales profitCalculate(int price, ShippingSize shippingSize) {
    if (price < 1_000) {
    throw new IllegalArgumentException("priceʹ͸1,000ԁҎ্Λઃఆ͍ͯͩ͘͠͞ɻ");
    }
    Sales sales = new Sales();
    sales.price = price;
    // ख਺ྉܭࢉ
    int fee = feeCalculate(sales.price);
    // ഑ૹྉܭࢉ
    int deliveryFee = deliveryFeeCalculate(shippingSize);
    sales.fee = fee;
    sales.deliveryFee = deliveryFee;
    sales.profit = price - fee - deliveryFee;
    return sales;
    }

    View Slide

  20. ςετίʔυΛ࣮ߦ
    ϦϑΝΫλϦϯάΛϛεΔͱςετΤϥʔ͕ൃੜ͠·͢

    View Slide

  21. मਖ਼͢Δ
    ޡ͍ͬͯͨՕॴΛमਖ਼͠·͢ɻ
    /**
    * ൢചརӹܭࢉ
    *
    * @param price ঎඼୅ۚ
    * @param shippingSize ঎඼αΠζ
    * @return ച্
    */
    public Sales profitCalculate(int price, ShippingSize shippingSize) {
    if (price < 1_000) {
    throw new IllegalArgumentException("priceʹ͸1,000ԁҎ্Λઃఆ͍ͯͩ͘͠͞ɻ");
    }
    Sales sales = new Sales();
    sales.price = price;
    // ख਺ྉܭࢉ
    int fee = feeCalculate(sales);
    // ഑ૹྉܭࢉ
    int deliveryFee = deliveryFeeCalculate(sales, shippingSize);
    // ഑ૹྉ͕300ԁҎ্ͳΒख਺ྉ͔Β100ԁׂҾ
    if (deliveryFee >= 300) {
    fee = max(fee - 100, 100);
    }
    sales.fee = fee;
    sales.deliveryFee = deliveryFee;
    sales.profit = price - fee - deliveryFee;
    return sales;
    }
    ͕͜͜࿙Ε͍ͯͨ

    View Slide

  22. ςετίʔυΛ࣮ߦ
    ςετΛ࣮ߦͯ͠શͯQBTTͨ͠ΒϦϑΝΫλϦϯά੒ޭͰ
    ͢ɻ

    View Slide

  23. ͜ΕͰϨΨγʔίʔυΛվળ͍ͯͧ͘͠ʂ

    View Slide

  24. ͜ΕͰϨΨγʔίʔυΛվળ͍ͯͧ͘͠ʂ
    ͋Εʁվળ͍ͯ͠Δ͸ͣͳͷʹ
    ϨΨγʔίʔυશવݮΒͳ͍͔΋ʁ

    View Slide

  25. ݸਓͷϦϑΝΫλϦϯάʹ͸ݶք͕͋Δ
    Ұਓ͕ςετΛ࡞੒ͯ͠ϦϑΝΫλϦϯάΛߦͳͬͯ΋ɺଞ
    ͷਓ͕৽ͨʹϨΨγʔίʔυΛ૿΍͢εϐʔυͷํ͕ૣ͍Մ
    ೳੑ͕͋Γ·͢ɻ
    ࠷ॳ͸෦԰ͷ૭ׂ͕Ε͍ͯΔ͚͕ͩͩͬͨɺ࣍ୈʹ͍͔ͭ͘ͷ෦԰ͷ૭ׂ͕Ε͍ͯΔঢ়ଶʹͳΔ
    ׂΕ૭ཧ࿦

    View Slide

  26. ςετίʔυΛॻ͘จԽΛ࡞Δ
    νʔϜͷଟ͘ͷਓ͕ςετίʔυΛॻ͘Α͏ʹͳΕ͹
    ɾόά͕গͳ͘ͳΔ
    ɾϦϑΝΫλϦϯά͠΍͘͢ͳΔ
    ɾྑ͍ίʔυ͕૿͑Δ
    ϨΨγʔίʔυͷ૿Ճεϐʔυྑ͍ίʔυͷ૿Ճεϐʔυ
    ʹͳͬͯγεςϜ͕վળ͞Ε͍͖ͯ·͢

    View Slide

  27. Ͳ͏΍ͬͯςετίʔυจԽΛ࡞Δʁ
    ࢲ͸νʔϜʹςετίʔυΛॻ͘จԽΛ࡞ΔͨΊʹ
    ҎԼͷΑ͏ͳ͜ͱΛ࣮ࢪ͠·ͨ͠ɻ
    ɾڧ੍తʹςετίʔυΛҙࣝ͢Δ؀ڥͷ੔උ
    ɾςετίʔυ͕ޮՌతʹॻ͚͍ͯΔ͔ΛՄࢹԽ
    ɾςετίʔυ࡞੒Λָʹ͢ΔϥΠϒϥϦͷಋೖ

    View Slide

  28. Ͳ͏΍ͬͯςετίʔυจԽΛ࡞Δʁ
    ࢲ͸νʔϜʹςετίʔυΛॻ͘จԽΛ࡞ΔͨΊʹ
    ҎԼͷΑ͏ͳ͜ͱΛ࣮ࢪ͠·ͨ͠ɻ
    ɾڧ੍తʹςετίʔυΛҙࣝ͢Δ؀ڥͷ੔උ
    ɾςετίʔυ͕ޮՌతʹॻ͚͍ͯΔ͔ΛՄࢹԽ
    ɾςετίʔυ࡞੒Λָʹ͢ΔϥΠϒϥϦͷಋೖ

    View Slide

  29. $*ͷಋೖ
    σϓϩΠϑϩʔʹ$* ςετ࣮ߦ
    Λಋೖ͠ɺ
    ςετʹࣦഊͨ͠ΒΤϥʔʹͯ͠σϓϩΠͤ͞ͳ͍͜ͱͰ
    ڧ੍తʹςετΛҙࣝ͢Δঢ়ଶΛ࡞Γ·ͨ͠ɻ
    ίϛοτ Ϗϧυ ςετ σϓϩΠ
    ಛఆͷϒϥϯνʹϚʔδ͞Εͨࡍʹ
    ࣗಈͰςετ͢Δ

    View Slide

  30. $*ͷಋೖ
    (SBEMF΍.BWFOͰ$*Λಋೖ͢Δͷʹಛผͳઃఆ͸ඞཁͳ͠
    ςετίʔυ͑͋͞Ε͹ɺCVJMEͱಉ࣌ʹςετ΋࣮ߦ͞Ε
    ·͢ɻ
    $ ./gradlew --console=plain build
    > Task :compileJava UP-TO-DATE
    > Task :processResources UP-TO-DATE
    > Task :classes UP-TO-DATE
    > Task :bootJarMainClassName UP-TO-DATE
    > Task :bootJar UP-TO-DATE
    > Task :jar UP-TO-DATE
    > Task :assemble UP-TO-DATE
    > Task :spotlessInternalRegisterDependencies UP-TO-DATE
    > Task :spotlessJava UP-TO-DATE
    > Task :spotlessJavaCheck UP-TO-DATE
    > Task :spotlessCheck UP-TO-DATE
    > Task :compileTestJava UP-TO-DATE
    > Task :processTestResources NO-SOURCE
    > Task :testClasses UP-TO-DATE
    > Task :test UP-TO-DATE
    > Task :check UP-TO-DATE
    > Task :build UP-TO-DATE
    BUILD SUCCESSFUL in 1s
    10 actionable tasks: 10 up-to-date
    UFTUλεΫ͕࣮ߦ͞ΕΔ

    View Slide

  31. Ͳ͏΍ͬͯςετίʔυจԽΛ࡞Δʁ
    ࢲ͸νʔϜʹςετίʔυΛॻ͘จԽΛ࡞ΔͨΊʹ
    ҎԼͷΑ͏ͳ͜ͱΛ࣮ࢪ͠·ͨ͠ɻ
    ɾڧ੍తʹςετίʔυΛҙࣝ͢Δ؀ڥͷ੔උ
    ɾςετίʔυ͕ޮՌతʹॻ͚͍ͯΔ͔ΛՄࢹԽ
    ɾςετίʔυ࡞੒Λָʹ͢ΔϥΠϒϥϦͷಋೖ

    View Slide

  32. ΧόϨοδΛՄࢹԽ
    ΧόϨοδΛՄࢹԽ͢Δ͜ͱͰ
    ޮՌతʹςετίʔυ͕ॻ͚͍ͯΔ͔͕෼͔Γɺ
    ·ͨ਺஋ͱͯ݁͠Ռ͕ग़Δ͜ͱͰϞνϕʔγϣϯʹܨ͕Δ͔
    ΋ͱߟ͍͑ͯ·͢ɻ
    ҎԼͰ͸+B$P$PͰग़ྗͨ͠ϨϙʔτΛද͍ࣔͯ͠·͢ɻ

    View Slide

  33. +B$P$Pͷಋೖ
    (SBEMF΍.BWFOʹ͸+B$P$Pͷ1MVHJOΛ௥Ճͯ͋͛͠Ε͹
    ར༻ՄೳͰ͢ɻৄࡉ͸ϦϯΫΛ͝ཡ͍ͩ͘͞ɻ
    plugins {
    id 'jacoco'
    }
    jacocoTestReport {
    dependsOn test
    reports {
    xml.required = false
    csv.required = false
    html.required = true
    }
    }

    org.jacoco
    jacoco-maven-plugin
    0.8.9-SNAPSHOT





    org.jacoco
    jacoco-maven-plugin



    report







    CVJMEHSBEMF QPNYNM
    IUUQTEPDTHSBEMFPSHDVSSFOUVTFSHVJEFKBDPDP@QMVHJOIUNM
    IUUQTXXXKBDPDPPSHKBDPDPUSVOLEPDNBWFOIUNM

    View Slide

  34. Ͳ͏΍ͬͯςετίʔυจԽΛ࡞Δʁ
    ࢲ͸νʔϜʹςετίʔυΛॻ͘จԽΛ࡞ΔͨΊʹ
    ҎԼͷΑ͏ͳ͜ͱΛ࣮ࢪ͠·ͨ͠ɻ
    ɾڧ੍తʹςετίʔυΛҙࣝ͢Δ؀ڥͷ੔උ
    ɾςετίʔυ͕ޮՌతʹॻ͚͍ͯΔ͔ΛՄࢹԽ
    ɾςετίʔυ࡞੒Λָʹ͢ΔϥΠϒϥϦͷಋೖ

    View Slide

  35. ςετίʔυಋೖͷ՝୊
    ɾॻ͍ͨ͜ͱ͕ͳ͍ਓͷ৺ཧతϋʔυϧ
    ɾϨΨγʔίʔυʹςετίʔυΛॻ͘ͷ͕େม
    ςετ͕૝ఆ͞ΕͨίʔυͰ͸ͳ͍ͨΊςετίʔυͷ࡞੒ʹ͕͔͔࣌ؒΔ
    ։ൃʹ͔͔࣌ؒΔ
    ΊΜͲͦ͘͞͏

    View Slide

  36. ςετίʔυಋೖͷ՝୊
    ɾॻ͍ͨ͜ͱ͕ͳ͍ਓͷ৺ཧతϋʔυϧ
    ɾϨΨγʔίʔυʹςετίʔυΛॻ͘ͷ͕େม
    ςετ͕૝ఆ͞ΕͨίʔυͰ͸ͳ͍ͨΊςετίʔυͷ࡞੒ʹ͕͔͔࣌ؒΔ
    ։ൃʹ͔͔࣌ؒΔ
    ΊΜͲͦ͘͞͏
    ָΛ͢ΔͨΊʹϥΠϒϥϦΛಋೖ͠Α͏ʂ
    ςετϥΠϒϥϦͷಋೖͰ͋Ε͹ɺϓϩμΫγϣϯίʔυ΁
    ͷӨڹ΋ݶఆతͰൺֱతؾܰʹಋೖͰ͖·͢

    View Slide

  37. ϞοΫΛ࢖͏
    %#ૢ࡞ɺෳࡶͳϝιουΛݺͼग़͢ͳͲςετίʔυΛॻ
    ͘ͷʹۤ࿑͢Δίʔυ΋ϞοΫΛར༻͢Ε͹؆୯ʹςετՄ
    ೳʹͳΓ·͢ɻ
    /**
    * ׂҾֹۚͷܭࢉ
    *
    * @param userId
    * @return ׂҾֹۚ
    */
    public int discount(String userId) {
    LocalDate toDate = localDateService.now();
    LocalDate fromDate = toDate.minusDays(7);
    List purchases =
    purchaseRepository.findByPurchaseDateBetween(userId, fromDate, toDate);
    if (purchases.size() >= 5) {
    return 200;
    }
    if (purchases.size() >= 3) {
    return 100;
    }
    return 0;
    }

    View Slide

  38. ϞοΫΛ࢖͏
    ϞοΫΛར༻ͨ͠ςετ͸͜ͷΑ͏ͳܗʹͳΓ·͢ɻ
    .PDLJUPΛར༻ͨ͠ྫ
    class DiscountServiceTest {
    @Mock private LocalDateService localDateService;
    @Mock private PurchaseRepository purchaseRepository;
    @InjectMocks private DiscountService discountService;
    @BeforeEach
    void beforeEach() {
    MockitoAnnotations.openMocks(this);
    }
    @Test
    void औҾ݅਺͕5݅ͷ৔߹͸200ԁׂҾ() {
    String userId = "123";
    LocalDate toDate = LocalDate.of(2022, 6, 13);
    when(localDateService.now()).thenReturn(toDate);
    LocalDate fromDate = LocalDate.of(2022, 6, 6);
    List purcases =
    IntStream.range(1, 6).mapToObj(i -> new Purchase(toDate, 100 * i)).toList();
    when(purchaseRepository.findByPurchaseDateBetween(userId, fromDate, toDate))
    .thenReturn(purcases);
    int actual = discountService.discount(userId);
    assertEquals(200, actual);
    verify(localDateService).now();
    verify(purchaseRepository).findByPurchaseDateBetween(userId, fromDate, toDate);
    }
    }
    ϝιουͷ໭Γ஋ΛϞοΫԽ

    View Slide

  39. ΞαʔγϣϯϥΠϒϥϦΛ࢖͏
    +6OJUʹσϑΥϧτͰ༻ҙ͞Ε͍ͯΔΞαʔγϣϯͩͱςε
    τίʔυͷ࡞੒͕ෳࡶʹͳΔ͜ͱ͕͋Γ·͢ɻ
    List actual = personService.ranking();
    List taroFilterdActual =
    actual.stream()
    .filter(person -> person.getName().equals("taro") && person.getAge() == 20)
    .toList();
    assertEquals(1, taroFilterdActual.size());
    List hanakoFilterdActual =
    actual.stream()
    .filter(person -> person.getName().equals("hanako") && person.getAge() == 25)
    .toList();
    assertEquals(1, hanakoFilterdActual.size());
    1FSTPOΫϥε͸
    FRVBMTϝιουΛ༻ҙ͍ͯ͠ͳ͍

    View Slide

  40. ΞαʔγϣϯϥΠϒϥϦΛ࢖͏
    +6OJUʹσϑΥϧτͰ༻ҙ͞Ε͍ͯΔΞαʔγϣϯͩͱςε
    τίʔυͷ࡞੒͕ෳࡶʹͳΔ͜ͱ͕͋Γ·͢ɻ
    List actual = personService.ranking();
    List taroFilterdActual =
    actual.stream()
    .filter(person -> person.getName().equals("taro") && person.getAge() == 20)
    .toList();
    assertEquals(1, taroFilterdActual.size());
    List hanakoFilterdActual =
    actual.stream()
    .filter(person -> person.getName().equals("hanako") && person.getAge() == 25)
    .toList();
    assertEquals(1, hanakoFilterdActual.size());
    List actual = personService.ranking();
    Person taro = new Person(0L, "taro", 20);
    Person hanako = new Person(0L, "hanako", 25);
    assertThat(actual)
    .usingRecursiveFieldByFieldElementComparatorOnFields("name", "age")
    .contains(taro, hanako);
    ΞαʔγϣϯϥΠϒϥϦΛར༻͢Δ͜ͱͰ؆୯ʹॻ͚ΔՄೳੑ͕͋Γ·͢ɻ
    ҎԼ͸"TTFSU+Λར༻ͨ͠ྫͰ͢ɻ
    1FSTPOΫϥε͸
    FRVBMTϝιουΛ༻ҙ͍ͯ͠ͳ͍

    View Slide

  41. ·ͱΊ
    w +6OJUΛ࢖͑͹҆શʹϦϑΝΫλϦϯάͰ͖Δ
    w ݸਓͰͷϦϑΝΫλϦϯάʹ͸ݶք͕͋ΔͨΊςετίʔυΛॻ
    ͘จԽΛ࡞Δ
    w $*Λಋೖͯ͠ςετίʔυΛڧ੍తʹҙࣝͤ͞Δ
    w ΧόϨοδΛՄࢹԽͯ͠ޮՌతʹςετίʔυ͕ॻ͚͍ͯΔ͔
    ֬ೝͰ͖Δ؀ڥΛ࡞Δ
    w ςετίʔυΛָʹ͢ΔϥΠϒϥϦΛಋೖͯ͠ɺςετίʔυ
    ࡞੒΁ͷϋʔυϧΛԼ͛Δ

    View Slide

  42. ͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ɻ

    View Slide