Last active
January 19, 2018 12:51
-
-
Save fumiyasac/47ec9f322f9f2eff8fcd79881b0051fe to your computer and use it in GitHub Desktop.
個人開発及び実務でユニットテストに助けられたと個人的に感じた例の紹介 ref: https://qiita.com/fumiyasac@github/items/19c525116f1b585bb43f
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public struct CalculateCalendarLogic { | |
/** | |
* | |
* 祝日になる日を判定する | |
* (引数) year: Int, month: Int, day: Int, weekdayIndex: Int | |
* weekdayIndexはWeekdayのenumに該当する値(0...6)が入る | |
* ※1. カレンダーロジックの参考:http://p-ho.net/index.php?page=2s2 | |
* ※2. 書き方(タプル)の参考:http://blog.kitoko552.com/entry/2015/06/17/213553 | |
* ※3. [Swift] 関数における引数/戻り値とタプルの関係:http://dev.classmethod.jp/smartphone/swift-function-tupsle/ | |
* | |
*/ | |
public func judgeJapaneseHoliday(year: Int, month: Int, day: Int) -> Bool { | |
・・・(省略)・・・ | |
//(1).9月15日(1966年から2002年まで)、(2).9月の第3月曜日(2003年から): 敬老の日 | |
case (1966...2002, 9, 15, _): | |
return true | |
case (year, 9, 15...21, .mon) where year > 2002: | |
return true | |
//9月16日: 敬老の日の振替休日 | |
case (1973...2002, 9, 16, .mon): | |
return true | |
//9月22日 or 23日: 秋分の日(計算値によって算出) | |
case (year, 9, day, _) | |
where PublicHolidaysLawYear <= year && day == SpringAutumn.autumn.calcDay(year: year): | |
return true | |
//秋分の日の次が月曜日: 振替休日 | |
case (year, 9, day, .mon) | |
where year >= AlternateHolidaysLawYear && day == SpringAutumn.autumn.calcDay(year: year) + 1: | |
return true | |
//シルバーウィークの振替休日である(※現行の法律改正から変わらないと仮定した場合2009年から発生する) | |
//See also: https://ja.wikipedia.org/wiki/シルバーウィーク | |
case (_, 9, _, _) | |
where oldPeopleDay(year: year) < day | |
&& day < SpringAutumn.autumn.calcDay(year: year) | |
&& getAlterHolidaySliverWeek(year: year) && year > 2008: | |
return true | |
・・・(省略)・・・ | |
} | |
} | |
/** | |
* | |
* シルバーウィークの振替休日を判定する | |
* 敬老の日の2日後が秋分の日ならば間に挟まれた期間は国民の休日とする | |
* | |
*/ | |
private func getAlterHolidaySliverWeek(year: Int) -> Bool { | |
return oldPeopleDay(year: year) + 2 == SpringAutumn.autumn.calcDay(year: year) | |
} | |
/** | |
* 指定した年の敬老の日を調べる | |
*/ | |
internal func oldPeopleDay(year: Int) -> Int { | |
let cal = Calendar.current as NSCalendar | |
func dateFromDay(day: Int) -> NSDate? { | |
return cal.date(era: AD, year: year, month: 9, day: day, hour: 0, minute: 0, second: 0, nanosecond: 0) as NSDate? | |
} | |
func weekdayAndDayFromDate(date: NSDate) -> (weekday: Int, day: Int) { | |
return ( | |
weekday: cal.component(.weekday, from: date as Date), | |
day: cal.component(.day, from: date as Date) | |
) | |
} | |
let monday = 2 | |
return (15...21) | |
.map(dateFromDay) | |
.flatMap{ $0 } | |
.map(weekdayAndDayFromDate) | |
.filter{ $0.weekday == monday } | |
.first! | |
.day | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import XCTest | |
@testable import CalculateCalendarLogic | |
class CalculateCalendarLogicTests: XCTestCase { | |
・・・(省略)・・・ | |
/** | |
* | |
* 春分の日・秋分の日の組み合わせが正しいかのテスト | |
* 計算式算出の参考:http://koyomi8.com/reki_doc/doc_0330.htm | |
* テストケース参考:http://www.nao.ac.jp/faq/a0301.html | |
* | |
*/ | |
func testShunbunAndShubun() { | |
let test = CalculateCalendarLogic() | |
// テストの目的:設定した日付が祝祭日となっていることをチェックする | |
let testCases: [(Int,Int,Int,Bool)] = [ | |
// 2000年 | |
(2000, 3, 20, true), | |
(2000, 9, 23, true), | |
・・・(※2000年〜2030年までのテストケースを準備する)・・・ | |
// 2030年 | |
(2030, 3, 20, true), | |
(2030, 9, 23, true) | |
] | |
testCases.forEach { (arg) in | |
let (year, month, day, expected) = arg | |
let result = test.judgeJapaneseHoliday(year: year, month: month, day: day) | |
guard let weekday = Weekday(year: year, month: month, day: day) else { XCTFail() ; return } | |
let message = "\(year)年\(month)月\(day)日(\(weekday.longName)):\(result)" | |
// judgeJapaneseHolidayメソッドの戻り値がBoolなので期待した結果に合致するかを判定する | |
if expected { | |
XCTAssertTrue (result, message) | |
} else { | |
XCTAssertFalse(result, message) | |
} | |
} | |
} | |
・・・(省略)・・・ | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import XCTest | |
@testable import #project-name# | |
import RealmSwift | |
/** | |
* 食材ごとのリスト画面のテーブル表示用に使用するメソッド: | |
* | |
* Ingredient.getArraysForIngredientListByCategory(category: カテゴリ定義したenumが入る) | |
* に関するテストケース | |
* | |
* case1. すべて | |
* case2. カテゴリーA | |
* | |
* の2ケースに対してユニットテストを行っている。 | |
* | |
* ※ 引数の指定がない場合は全部の食材内容が表示されるので異常系の処理に関しては考慮していない | |
*/ | |
class IngredientTests: XCTestCase { | |
//カテゴリー選択:カテゴリーA | |
let selectedCategory: IngredientListCategoryEnum = IngredientListCategoryEnum.categoryA | |
//食材IDが7のものを取得する(カテゴリーAに含まれている) | |
let categoryAEntity: IngredientEntity? = Ingredient.findById(7) | |
//カテゴリーAのIDは11である | |
let categoryACategory1Id = 11 | |
override func setUp() { | |
super.setUp() | |
} | |
override func tearDown() { | |
super.tearDown() | |
} | |
func testGetArraysForIngredientListByCategory() { | |
//その1: 引数なしの場合 | |
let ingredientListByCategoryAboutAll = Ingredient.getArraysForIngredientListByCategory() | |
XCTAssertNotEqual(0, ingredientListByCategoryAboutAll.0.count, "セル表示用オブジェクトの配列が0件でないこと") | |
XCTAssertNotEqual(0, ingredientListByCategoryAboutAll.1.count, "食材リストのセクション表示の配列が0件でないこと") | |
XCTAssertNotNil(ingredientListByCategoryAboutAll.0.first?.first?.ingredient?.name, "セル表示用オブジェクトから取得した食材名がnilでないこと") | |
XCTAssertNotNil(ingredientListByCategoryAboutAll.1.first?.id, "食材リストのセクション表示のカテゴリIDが取得できること") | |
//その2: カテゴリー選択がカテゴリーAの場合 | |
let ingredientListByCategoryA = Ingredient.getArraysForIngredientListByCategory(category: selectedCategory) | |
XCTAssertNotEqual(0, ingredientListByCategoryA.0.count, "セル表示用オブジェクトの配列が0件でないこと") | |
XCTAssertNotEqual(0, ingredientListByCategoryA.1.count, "食材リストのセクション表示の配列が0件でないこと") | |
//カテゴリーAの食材データのみを抽出 | |
var ingredientEntitiesOfCategoryA: [IngredientEntity] = [] | |
for (_, ingredientCellObjectList) in ingredientListByCategoryA.0.enumerated() { | |
let ingredientCellObjects = ingredientCellObjectList.map({ $0.ingredient }) | |
for (_, targetCategoryAEntity) in ingredientCellObjects.enumerated() { | |
ingredientEntitiesOfCategoryA.append(targetCategoryAEntity!) | |
} | |
} | |
XCTAssertTrue(ingredientEntitiesOfCategoryA.contains(categoryAEntity!), "カテゴリーIDがカテゴリーAの食材が含まれていること") | |
let ingredientCategory1CategoryAName = ingredientEntitiesOfCategoryA.1.map({ $0.id }).first | |
XCTAssertEqual(CategoryACategory1Id, ingredientCategory1CategoryAName, "取得できたカテゴリーIDがカテゴリーAのものと一致すること") | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import XCTest | |
@testable import #project-name# | |
import RealmSwift | |
class SearcherTests: XCTestCase { | |
override func setUp() { | |
super.setUp() | |
} | |
override func tearDown() { | |
super.tearDown() | |
} | |
func testGetRecipes() { | |
//その1: 完全一致するレシピが存在する場合はレシピデータの先頭にくる | |
let searchResultsOne = Searcher.getRecipes( | |
PeriodTypeEnum.all, | |
searchWord: "XXX", | |
notSearchWord: "", | |
limitationFlg: false, | |
excludeFlgs: [] | |
) | |
XCTAssertEqual( | |
Recipe.findById(100), //レシピID:100 → レシピ名:XXX | |
searchResultsOne.first, | |
"検索に含めるワード「XXX」で完全一致検索を行った際には、「XXX」のデータが先頭にくること" | |
) | |
//その2: 完全一致する語句が存在する場合 | |
let searchResultsTwo = Searcher.getRecipes( | |
PeriodTypeEnum.all, | |
searchWord: "バナナミルク", | |
notSearchWord: "", | |
limitationFlg: false, | |
excludeFlgs: ["milk"] //検索から外したい項目で「牛乳」を選んでいる | |
) | |
XCTAssertNotEqual( | |
Recipe.findById(511), //レシピID:511 → レシピ名:バナナミルク | |
searchResultsTwo.first, | |
"検索から外したい項目で「牛乳」を選び検索に含めるワード「バナナミルク」で完全一致検索を行った際には、バナナミルクのデータが先頭にこないこと" | |
) | |
//その3: 除外する食材のチェックボックスを有効にした食材を使用している検索ワードで検索する場合 | |
let searchResultsThree = Searcher.getRecipes( | |
PeriodTypeEnum.latter, | |
searchWord: "納豆", | |
notSearchWord: "", | |
limitationFlg: false, | |
excludeFlgs: ["soy"] //検索から外したい項目で「大豆」を選んでいる | |
) | |
XCTAssertEqual( | |
[], | |
searchResultsThree, | |
"検索から外したい項目で「大豆」を選び検索に含めるワードに「納豆」を設定した際には、1件もデータを取得できないこと" | |
) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* ViewController.swift */ | |
// Step1: ライブラリのインポート(手動で導入した場合は不要) | |
import CalculateCalendarLogic | |
// Step2: CalculateCalendarLogicのインスタンスを作成 | |
let holiday = CalculateCalendarLogic() | |
... | |
// Step3: 使用する際は引数を入れての判定を行う | |
let result: Bool = holiday.judgeJapaneseHoliday(year: 2016, month: 1, day: 1) | |
// 実行結果 | |
print("2016年1月1日:\(result)") | |
//コンソールでは 「2016年1月1日:true」 と表示されます |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment