Swiftにおける本当にRSpecライクなテストの書き方 / RSpec-like Test in Swift

070d33664df274636dad3da21c9b21fc?s=47 rockname
December 10, 2018

Swiftにおける本当にRSpecライクなテストの書き方 / RSpec-like Test in Swift

070d33664df274636dad3da21c9b21fc?s=128

rockname

December 10, 2018
Tweet

Transcript

  1. ~Better Specs ʹֶͿ~ Swiftʹ͓͚Δຊ౰ʹ
 RSpecϥΠΫͳςετͷॻ͖ํ iOS Test Night #9 -

    2प೥ - 2018/12/10 @rockname
  2. @rockname גࣜձࣾϛΫγΟ / ΈͯͶࣄۀ෦ ΞϓϦ։ൃG 18৽ଔ iOS(Swift, objc) > Android(Kotlin,

    Java) == Rails(ruby) ˎॻ͘ྔͷॱ൪
  3. ΈͯͶͰ͸νʔϜશମͰ
 ςετΛॻ͘จԽ͕
 ࠜ෇͍͍ͯΔ

  4. ςετ͕ͳ͔ͬͨΒbotʹἤΒΕΔ

  5. iOSͷςετ؀ڥ • ςετϑϨʔϜϫʔΫͱͯ͠ Quick/Nimble Λ࠾༻ • RSpecʹΠϯεύΠΞ͞ΕͨBDD Framework • ΈͯͶ͸ServerͷςετΛRSpecͰॻ͍͍ͯΔͷͰ׳Ε͍ͯΔਓ͕ؒ

    ଟ͍ • Mock͸… • ಛʹϥΠϒϥϦ͸࢖͍ͬͯͳ͍ (Φεεϝ͕͋Ε͹ڭ͍͑ͯͩ͘͞) • Mock ObjectΛςετ͝ͱʹఆ͍ٛͯ͠Δ
  6. BDDͰςετΛॻ͘ͳΒ
 ಡΈ΍͍͢ςετΛॻ͖͍ͨ…ʂ

  7. Better Specs

  8. Better Specs ͱ͸ʁ • RSpecʹ͓͚Δྑ͍ςετͷ࡞ΓํΛूΊͨΨΠυϥΠϯ • GitHubͰopenʹ؅ཧ͞Ε͍ͯΔ • ॻ͖ํʹٙ໰͕͋Ε͹issue্Ͱٞ࿦͢Δ͜ͱ͕Ͱ͖Δ •

    RSpecͷॻ͖ํʹ໎ͬͨΒྑ͘ݟΔ
  9. RSpecʹӨڹ͞ΕͨQuickͰ΋ Better Specs ʹͳΒͬͯ
 ՄಡੑΛ্͛Δͧ

  10. FizzBuzzؔ਺ͷςετΛॻ͘ • Ҿ਺ʹIntΛऔΓɺ • 3ͷഒ਺ͷ࣌ -> “Fizz” • 5ͷഒ਺ͷ࣌ ->

    “Buzz” • 3ͱ5ͷެഒ਺ͷ࣌ -> “FizzBuzz” • 0ҎԼͷ࣌ -> “out of range” • ͦΕҎ֎ͷ࣌ -> ༩͑ΒΕͨ਺ࣈΛͦͷ··ฦ͢
  11. FizzBuzzؔ਺ enum FizzBuzz { static func fizzBuzz(_ number: Int) ->

    String { guard number > 0 else { return "out of range” } switch number { case let n where n % 15 == 0: return "FizzBuzz" case let n where n % 5 == 0: return "Buzz" case let n where n % 3 == 0: return "Fizz" default: return String(number) } } }
  12. ·ͣ͸Կ΋ߟ͑ͣʹͱΓ͋͑ͣ
 ςετΛॻ͍ͯΈΔ ✍

  13. ͳʹ΋ߟ͑ͣʹॻ͍ͨςετ class FizzBuzzSpec: QuickSpec { override func spec() { describe("#fizzBuzz")

    { it("should meet all FizzBuzz specifications") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) expect(FizzBuzz.fizzBuzz(-1)) .to(equal(“out of range")) expect(FizzBuzz.fizzBuzz(1)).to(equal("1")) } } } }
  14. ͳʹ΋ߟ͑ͣʹॻ͍ͨςετ class FizzBuzzSpec: QuickSpec { override func spec() { describe("#fizzBuzz")

    { it("should meet all FizzBuzz specifications") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) expect(FizzBuzz.fizzBuzz(-1)) .to(equal(“out of range")) expect(FizzBuzz.fizzBuzz(1)).to(equal("1")) } } } } ςετର৅ͷϝιου
  15. ͳʹ΋ߟ͑ͣʹॻ͍ͨςετ class FizzBuzzSpec: QuickSpec { override func spec() { describe("#fizzBuzz")

    { it("should meet all FizzBuzz specifications") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) expect(FizzBuzz.fizzBuzz(-1)) .to(equal(“out of range")) expect(FizzBuzz.fizzBuzz(1)).to(equal("1")) } } } } ظ଴͞ΕΔϝιουͷ;Δ·͍
  16. ͳʹ΋ߟ͑ͣʹॻ͍ͨςετ class FizzBuzzSpec: QuickSpec { override func spec() { describe("#fizzBuzz")

    { it("should meet all FizzBuzz specifications") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) expect(FizzBuzz.fizzBuzz(-1)) .to(equal(“out of range")) expect(FizzBuzz.fizzBuzz(1)).to(equal("1")) } } } } 3ͷഒ਺ͷ࣌ -> “Fizz”
  17. ͳʹ΋ߟ͑ͣʹॻ͍ͨςετ class FizzBuzzSpec: QuickSpec { override func spec() { describe("#fizzBuzz")

    { it("should meet all FizzBuzz specifications") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) expect(FizzBuzz.fizzBuzz(-1)) .to(equal(“out of range")) expect(FizzBuzz.fizzBuzz(1)).to(equal("1")) } } } } 5ͷഒ਺ͷ࣌ -> “Buzz”
  18. ͳʹ΋ߟ͑ͣʹॻ͍ͨςετ class FizzBuzzSpec: QuickSpec { override func spec() { describe("#fizzBuzz")

    { it("should meet all FizzBuzz specifications") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) expect(FizzBuzz.fizzBuzz(-1)) .to(equal(“out of range")) expect(FizzBuzz.fizzBuzz(1)).to(equal("1")) } } } } 3ͱ5ͷެഒ਺ͷ࣌ -> “FizzBuzz”
  19. ͳʹ΋ߟ͑ͣʹॻ͍ͨςετ class FizzBuzzSpec: QuickSpec { override func spec() { describe("#fizzBuzz")

    { it("should meet all FizzBuzz specifications") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) expect(FizzBuzz.fizzBuzz(-1)) .to(equal(“out of range")) expect(FizzBuzz.fizzBuzz(1)).to(equal("1")) } } } } 0ҎԼͷ࣌ -> ”out of range”
  20. ͳʹ΋ߟ͑ͣʹॻ͍ͨςετ class FizzBuzzSpec: QuickSpec { override func spec() { describe("#fizzBuzz")

    { it("should meet all FizzBuzz specifications") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) expect(FizzBuzz.fizzBuzz(-1)) .to(equal(“out of range")) expect(FizzBuzz.fizzBuzz(1)).to(equal("1")) } } } } ͦΕҎ֎ͷ࣌ -> ༩͑ΒΕͨ਺ࣈ
  21. ͳʹ΋ߟ͑ͣʹॻ͍ͨςετ class FizzBuzzSpec: QuickSpec { override func spec() { describe("#fizzBuzz")

    { it("should meet all FizzBuzz specifications") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) expect(FizzBuzz.fizzBuzz(-1)) .to(equal(“out of range")) expect(FizzBuzz.fizzBuzz(1)).to(equal("1")) } } } }
  22. ͳʹ΋ߟ͑ͣʹॻ͍ͨςετ class FizzBuzzSpec: QuickSpec { override func spec() { describe("#fizzBuzz")

    { it("should meet all FizzBuzz specifications") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) expect(FizzBuzz.fizzBuzz(-1)) .to(equal(“out of range")) expect(FizzBuzz.fizzBuzz(1)).to(equal("1")) } } } } ύοͱݟͯ΋ϝιουͷৼΔ෣͍͕ ཧղ͠ʹ͍͘
  23. Better Specs 1. ୯Ұ৚݅ςετ
 2. ContextΛ࢖͏
 3. SubjectΛ࢖͏

  24. Better Specs 1. ୯Ұ৚݅ςετ
 2. ContextΛ࢖͏
 3. SubjectΛ࢖͏

  25. #FUUFS4QFDT

  26. #FUUFS4QFDT

  27. #FUUFS4QFDT

  28. ୯Ұ৚݅ςετ describe("#fizzBuzz") { it("should return Fizz when given multiples of

    3") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) } it("should return Buzz when given multiples of 5") { expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) } it("should return FizzBuzz when given multiples of both 3 and 5") { expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) } ɾ ɾ ɾ
  29. ୯Ұ৚݅ςετ describe("#fizzBuzz") { it("should return Fizz when given multiples of

    3") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) } it("should return Buzz when given multiples of 5") { expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) } it("should return FizzBuzz when given multiples of both 3 and 5") { expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) } ɾ ɾ ɾ 3ͷഒ਺ͷ࣌ -> “Fizz”
  30. ୯Ұ৚݅ςετ describe("#fizzBuzz") { it("should return Fizz when given multiples of

    3") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) } it("should return Buzz when given multiples of 5") { expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) } it("should return FizzBuzz when given multiples of both 3 and 5") { expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) } ɾ ɾ ɾ 5ͷഒ਺ͷ࣌ -> “Buzz”
  31. ୯Ұ৚݅ςετ describe("#fizzBuzz") { it("should return Fizz when given multiples of

    3") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) } it("should return Buzz when given multiples of 5") { expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) } it("should return FizzBuzz when given multiples of both 3 and 5") { expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) } ɾ ɾ ɾ 3ͱ5ͷެഒ਺ͷ࣌ -> “FizzBuzz”
  32. Better Specs 1. ୯Ұ৚݅ςετ
 2. ContextΛ࢖͏
 3. SubjectΛ࢖͏

  33. #FUUFS4QFDT

  34. #FUUFS4QFDT

  35. #FUUFS4QFDT

  36. ContextΛ࢖͏ describe("#fizzBuzz") { context("when given multiples of 3") { it("should

    return Fizz") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) } } context("when given multiples of 5") { it("should return Buzz") { expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) } } ɾ ɾ ɾ
  37. ContextΛ࢖͏ describe("#fizzBuzz") { context("when given multiples of 3") { it("should

    return Fizz") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) } } context("when given multiples of 5") { it("should return Buzz") { expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) } } ɾ ɾ ɾ 3ͷഒ਺ͷ࣌ -> “Fizz”
  38. ContextΛ࢖͏ describe("#fizzBuzz") { context("when given multiples of 3") { it("should

    return Fizz") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) } } context("when given multiples of 5") { it("should return Buzz") { expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) } } ɾ ɾ ɾ 5ͷഒ਺ͷ࣌ -> “Buzz”
  39. Better Specs 1. ୯Ұ৚݅ςετ
 2. ContextΛ࢖͏
 3. SubjectΛ࢖͏

  40. #FUUFS4QFDT

  41. #FUUFS4QFDT

  42. #FUUFS4QFDT

  43. #FUUFS4QFDT subject = ςετର৅ͷओମ

  44. SubjectΛ࢖͏ describe("#fizzBuzz") { var number: Int! var subject: String {

    return FizzBuzz.fizzBuzz(number) } context("when given multiples of 3") { beforeEach { number = 3 } it("should return Fizz") { expect(subject).to(equal("Fizz")) } } ɾ ɾ ɾ
  45. SubjectΛ࢖͏ describe("#fizzBuzz") { var number: Int! var subject: String {

    return FizzBuzz.fizzBuzz(number) } context("when given multiples of 3") { beforeEach { number = 3 } it("should return Fizz") { expect(subject).to(equal("Fizz")) } } ɾ ɾ ɾ Ҿ਺Λม਺ͱͯ͠ఆٛ subjectΛܭࢉܕϓϩύςΟͱͯ͠એݴ
  46. SubjectΛ࢖͏ describe("#fizzBuzz") { var number: Int! var subject: String {

    return FizzBuzz.fizzBuzz(number) } context("when given multiples of 3") { beforeEach { number = 3 } it("should return Fizz") { expect(subject).to(equal("Fizz")) } } ɾ ɾ ɾ beforeEach ͰcontextʹԠͨ͡
 ஋Λnumberม਺ʹ୅ೖ
  47. SubjectΛ࢖͏ describe("#fizzBuzz") { var number: Int! var subject: String {

    return FizzBuzz.fizzBuzz(number) } context("when given multiples of 3") { beforeEach { number = 3 } it("should return Fizz") { expect(subject).to(equal("Fizz")) } } ɾ ɾ ɾ ɹexpectʹ͸subjectͷΈ౉͢
  48. SubjectΛ࢖͏ describe("#fizzBuzz") { var number: Int! var subject: String {

    return FizzBuzz.fizzBuzz(number) } context("when given multiples of 3") { beforeEach { number = 3 } it("should return Fizz") { expect(subject).to(equal("Fizz")) } } ɾ ɾ ɾ
  49. Ͳ͏มΘͬͨͷ

  50. Before class FizzBuzzSpec: QuickSpec { override func spec() { describe("#fizzBuzz")

    { it("should meet all FizzBuzz specifications") { expect(FizzBuzz.fizzBuzz(3)).to(equal("Fizz")) expect(FizzBuzz.fizzBuzz(5)).to(equal("Buzz")) expect(FizzBuzz.fizzBuzz(15)).to(equal("FizzBuzz")) expect(FizzBuzz.fizzBuzz(-1)) .to(equal(“out of range")) expect(FizzBuzz.fizzBuzz(1)).to(equal("1")) } } } }
  51. After describe("#fizzBuzz") { var number: Int! var subject: String {

    return FizzBuzz.fizzBuzz(number) } context("when given multiples of 3") { beforeEach { number = 3 } it("should return Fizz") { expect(subject).to(equal("Fizz")) } } ɾ ɾ ɾ
  52. After describe("#fizzBuzz") { var number: Int! var subject: String {

    return FizzBuzz.fizzBuzz(number) } context("when given multiples of 3") { beforeEach { number = 3 } it("should return Fizz") { expect(subject).to(equal("Fizz")) } } ɾ ɾ ɾ Կ͕ (What)
  53. After describe("#fizzBuzz") { var number: Int! var subject: String {

    return FizzBuzz.fizzBuzz(number) } context("when given multiples of 3") { beforeEach { number = 3 } it("should return Fizz") { expect(subject).to(equal("Fizz")) } } ɾ ɾ ɾ Կ͕ (What) ͍ͭ (When)
  54. After describe("#fizzBuzz") { var number: Int! var subject: String {

    return FizzBuzz.fizzBuzz(number) } context("when given multiples of 3") { beforeEach { number = 3 } it("should return Fizz") { expect(subject).to(equal("Fizz")) } } ɾ ɾ ɾ ͍ͭ (When) Կ͕ (What) Ͳ͏ͳΔͷ͔ (How)
  55. After describe("#fizzBuzz") { var number: Int! var subject: String {

    return FizzBuzz.fizzBuzz(number) } context("when given multiples of 3") { beforeEach { number = 3 } it("should return Fizz") { expect(subject).to(equal("Fizz")) } } ɾ ɾ ɾ ͍ͭ (When) Կ͕ (What) Ͳ͏ͳΔͷ͔ (How)
  56. After describe("#fizzBuzz") { var number: Int! var subject: String {

    return FizzBuzz.fizzBuzz(number) } context("when given multiples of 3") { beforeEach { number = 3 } it("should return Fizz") { expect(subject).to(equal("Fizz")) } } ɾ ɾ ɾ ͍ͭ (When) Կ͕ (What) Ͳ͏ͳΔͷ͔ (How)
  57. ·ͱΊ • Quick͸RSpecʹΠϯεύΠΞ͞ΕͨBDD Framework • BetterSpecsʹͳΒͬͯॻ͘͜ͱͰɺͦͷϝιουͷ࢓༷͕ཧղ͠ ΍͘͢ͳΔςετΛॻ͚Δ (Spec = Specification

    = ࢓༷ॻ) • ͨͩɺ޷͖ݏ͍͸ඞͣ͋ΔͷͰڧ੍͸ྑ͘ͳ͍ 
 (XCTestͰ΋What/When/HowΛҙࣝͨ͠ςετ͸ॻ͚Δ)
  58. Thank you!!!