.entry .entry-content .table-of-contents > li > ul, .table-of-contents li:nth-child(2) { display: none; } ã¯ããã« ããã«ã¡ã¯ãECãã©ãããã©ãŒã éšã®æš©å®ã§ããæ®æ®µã¯ZOZOTOWNã®äŒå¡åºç€ãIDåºç€ã®éçºã«æºãã£ãŠããŸãã æ¬èšäºã§ã¯ãäŒå¡åºç€ã§å°å
¥ããããŒã¿ããŒã¹ãžã®æžã蟌ã¿ã䌎ãåŠçã®ãã¹ãææ³ã«ã€ããŠç޹ä»ããŸãããã®ææ³ã§ã¯å®è¡ååŸã®ããŒã¿ããŒã¹ã®å·®åã«æ³šç®ããããšã§ç¹å®ã®ã¬ã³ãŒãã ãã§ãªããããŒã¿ããŒã¹å
šäœãžã®å¯äœçšãç¶²çŸ
çã«æ€ç¥ããããšãç®çãšããŸãã ç®æ¬¡ ã¯ããã« ç®æ¬¡ åŸæ¥ææ³ã®èª²é¡ å·®åæ€èšŒã«ããã¢ãããŒã Goã«ãã差忀åºããŒã«ã®å®è£
å©çšã€ã¡ãŒãž å·®åæœåºã®å®è£
è€æ°ããŒã¿ããŒã¹ãžã®å¯Ÿå¿ å°å
¥æã®å·¥å€«ç¹ éåºå®å€ã®åãæ±ã æåŸ
å€ã®æ£èŠå å·®åã®é€å€ ãŸãšã åŸæ¥ææ³ã®èª²é¡ ããŒã¿ããŒã¹ãžã®æžã蟌ã¿ã䌎ãåŠçã®ãã¹ãã§ã¯ãäžè¬çã«ä»¥äžã®ããã«é¢æ°ã®è¿ãå€ãšåŠç察象ã§ããç¹å®ã®ã¬ã³ãŒããæ€èšŒããããšãå€ããšæããŸãã // 1. ãã¹ã察象ã®é¢æ°ãå®è¡ refundedPoints, err := usecase.CancelOrder(ctx, orderID) AssertEqual(t, nil , err) // 2. è¿éãã€ã³ãïŒè¿ãå€ïŒãæ€èšŒ AssertEqual(t, 500 , refundedPoints) // 3. ç¹å®ã®ã¬ã³ãŒãã®ç¶æ
ãæ€èšŒ order, _ := orderRepo.FindByID(ctx, orderID) AssertEqual(t, "CANCELLED" , order.Status) ãããããããã®ãã¹ãã ãã§ã¯ããŒã¿ããŒã¹ãžã®ãæåŸ
ããªãå¯äœçšããé²ããªãããšã«èª²é¡ãæããŠããŸãããäŸãã°ãæŽæ°ãåé€ã®æ¡ä»¶æå®ã«èª€ãããããšæ³å®å€ã®ã¬ã³ãŒãã«åœ±é¿ãåãŒãããšãèããããŸãããã®å Žåã«ã¯ãåŠç察象ã§ããç¹å®ã®ã¬ã³ãŒãã®ã¿ãæ€èšŒãããšããŠãããã®ä»ã®ã¬ã³ãŒããç Žå£ãããŠããããšã«æ°ã¥ãããšã¯ã§ããŸããã å·®åæ€èšŒã«ããã¢ãããŒã ãã®èª²é¡ã解決ããã«ã¯ãããŒã¿ããŒã¹ãžã®å¯äœçšãç¶²çŸ
çã«æ€èšŒããå¿
èŠããããŸããããã§ãããŒã¿ããŒã¹ã®å®è¡ååŸã®å
šã¬ã³ãŒãããã£ããã£ããŠæ¯èŒãããã®å·®åãæ€èšŒããã¢ãããŒããæ¡çšããŸããã ãã®ã¢ãããŒãã§ã¯ãç¹å®ã®ããŒãã«ã»ã¬ã³ãŒãã»ã«ã©ã ãèŠãã®ã§ã¯ãªããããŒã¿ããŒã¹å
šäœãžã®å¯äœçšããã¹ã察象ã®åºåã®1ã€ãšããŠæããŸããåºåã®æåŸ
å€ãšããŠå·®åãæå®ããæåŸ
ããå¯äœçšã®ã¿ãååšããããšãæ€èšŒããããšã§ãæåŸ
ããªãå¯äœçšãçããéã«ãããæ€ç¥ã§ããŸãã å·®åã¯ä»¥äžã®3çš®é¡ãšããŠããããæœåºããŸãã äœæ (Create)ïŒæ°èŠã¬ã³ãŒãã®ã«ã©ã å
šäœã®å€ãä¿æ æŽæ° (Update)ïŒããŒæ
å ±ãšã倿Žããã£ãã«ã©ã ã®ã倿ŽåŸã®å€ããä¿æ åé€ (Delete)ïŒã¬ã³ãŒããç¹å®ããããŒæ
å ±ãä¿æ æŽæ°ã®å·®åã®è¡šçŸãšããŠã¯ãæŽæ°ååŸã®å€ãå«ããæ¹ãäžè¬çã§ãããæ¬ææ³ã§ã¯ãããŠæŽæ°åŸã®å€ã®ã¿ãä¿æããŠããŸãããã¹ããšãã芳ç¹ã§ã¯ãæŽæ°åã®å€ã¯äºåæ¡ä»¶ã®äžéšã§ããããã¹ãããŒã¿ã®ã»ããã¢ããå
容ãšéè€ããããã§ãã Goã«ãã差忀åºããŒã«ã®å®è£
å©çšã€ã¡ãŒãž äŒå¡åºç€ã¯Goã§å®è£
ãããŠããããããã¹ããžã®çµã¿èŸŒã¿ããããèæ
®ããŠä»åã¯Goã®ã³ãŒãäžã§å®è£
ããŸããã 以äžã«ãã©ã®ããã«å·®åãGoã®æ§é äœã§è¡šçŸããå©çšãããã®ã€ã¡ãŒãžã瀺ããŸãã // å·®åããŒã¿ã®æ§é ã€ã¡ãŒãž type Diff struct { C []Record // CREATE: 远å ãããã¬ã³ãŒã矀ãæå®ãåã¬ã³ãŒãã¯å
šãã£ãŒã«ãå€ãæå® U map [KeyHash]Record // UPDATE: æŽæ°ã®ããã¬ã³ãŒã矀ãäž»ããŒã®ããã·ã¥å€ã§æå®ãåã¬ã³ãŒãã¯æŽæ°åŸã®ãã£ãŒã«ãå€ãæå® D []Record // DELETE: åé€ãããã¬ã³ãŒã矀ãæå®ãåã¬ã³ãŒãã¯äž»ããŒå€ãæå®ïŒäž»ããŒãååšããªãå Žåã¯å
šãã£ãŒã«ãå€ïŒ } type Diffs map [ string ]Diff // mapã®ããŒã¯ããŒãã«å // æ¿å
¥æã®å·®åæ€åºã®å©çšã€ã¡ãŒãž var result int diffs := DiffDB(ctx, db, func () { result = insertMember(member) }) AssertEqual(t, 1 , result) AssertRecords(t, Diffs{ "members" : { C: []Record{ { "id" : 1 , "age" : 20 , "nickname" : "taro" , }, }, }, }, diffs) // æŽæ°æã®å·®åæ€åºã®å©çšã€ã¡ãŒãž var result int diffs = DiffDB(ctx, db, func () { result = updateMemberName( 1 , "jiro" ) }) AssertEqual(t, 1 , result) AssertRecords(t, Diffs{ "members" : { U: map [KeyHash]Record{ HashKey({ "id" : 1 }): { "nickname" : "jiro" , }, }, }, }, diffs) // å逿ã®å·®åæ€åºã®å©çšã€ã¡ãŒãž var result int diffs = DiffDB(ctx, db, func () { result = deleteMember( 1 ) }) AssertEqual(t, 1 , result) AssertRecords(t, Diffs{ "members" : { D: []Record{ { "id" : 1 , }, }, }, }, diffs) ãã®ããã«ããŒã¿ããŒã¹ã«å¯Ÿããå¯äœçšãåºåå€ãšããŠæ€èšŒã§ããããããã¬ã³ãŒããååŸããŠç¹å®ã®ã«ã©ã ãæ€èšŒããããšããåœä»€çãªèšè¿°ãç¹°ãè¿ãå¿
èŠããªããªãããã¹ãã³ãŒãã®å¯èªæ§ãšä¿å®æ§ãåäžããŸãã å
·äœçã«ã¯ãGoã§åºãæ¡çšãããŠããããŒãã«é§åãã¹ãã®ã¹ã¿ã€ã«ãšèŠªåæ§ãé«ããè€æ°ã®ãã¹ãã±ãŒã¹ãç°¡æœã«èšè¿°ã§ããŸããäŸãã°ãæ¡ä»¶åå²ã§æžã蟌ãããŒãã«ãå€ãã颿°ããã¹ãããå ŽåãåŸæ¥ã®ææ³ã§ã¯ããã¹ãã±ãŒã¹ã«ãã£ãŠæ€èšŒåŠçãåå²ãããããã¹ãã±ãŒã¹ã®æ§é äœã«æ€èšŒåŠçãæã€å¿
èŠããããŸãããæ€èšŒåŠçã®åå²ã¯ãã¹ãã³ãŒãã®è€éåãæãããã¹ãã±ãŒã¹ã®æ§é äœã«æ€èšŒåŠçãæãããããšã¯ããŒãã«é§åãã¹ãã®ã¡ãªããã§ãã宣èšçãªèšè¿°ãæãªããŸãã ããããä»åå°å
¥ããææ³ã§ããã°å®£èšçãªèšè¿°ãç¶æã§ããŸãã以äžã«ããããã®ææ³ã®äŸã瀺ããŸãã // åŸæ¥ææ³ãã®1ïŒæ€èšŒåŠçã®æ¡ä»¶åå²ïŒ tests := [] struct { name string orderID string expectRefund bool // è¿éããŒãã«ã確èªãããã©ããã®ãã©ã° expectPointReset bool // ãã€ã³ãæŽæ°ã確èªãããã©ããã®ãã©ã° }{ { name: "ã¯ã¬ãžããã«ãŒã決æžã®ãã£ã³ã»ã«ïŒè¿éããïŒ" , orderID: "order_card" , expectRefund: true , expectPointReset: false , }, { name: "å
šé¡ãã€ã³ãæãã®ãã£ã³ã»ã«ïŒãã€ã³ãéå
ããïŒ" , orderID: "order_point" , expectRefund: false , expectPointReset: true , }, } for _, tt := range tests { t.Run(tt.name, func (t *testing.T) { // 1. ãã¹ã察象ã®å®è¡ err := usecase.CancelOrder(ctx, tt.orderID) AssertEqual(t, nil , err) // 2. 泚æã¹ããŒã¿ã¹ã®æ€èšŒïŒå
±éïŒ order, err := orderRepo.FindByID(ctx, tt.orderID) AssertEqual(t, nil , err) AssertEqual(t, "CANCELLED" , order.Status) // 3. æ¡ä»¶åå²ã«ããåå¥ããŒãã«ã®ã¢ãµãŒã·ã§ã³ // ãã¹ãã±ãŒã¹ãå¢ãããã³ã«ããã®åå²ããžãã¯ã®ã¡ã³ããã³ã¹ãå¿
èŠã«ãªã if tt.expectRefund { refund, err := refundRepo.FindByOrderID(ctx, tt.orderID) AssertEqual(t, nil , err) AssertEqual(t, Refund{OrderID: tt.orderID, amount: 1000 }, refund) } if tt.expectPointReset { user, err := userRepo.FindByID(ctx, order.UserID) AssertEqual(t, nil , err) AssertEqual(t, 1500 , user.Points) } }) } // åŸæ¥ææ³ãã®2ïŒãã¹ãã±ãŒã¹ããšã«æ€èšŒåŠçãæã€ïŒ tests := [] struct { name string orderID string assertFunc func (t *testing.T, orderID string ) }{ { name: "ã¯ã¬ãžããã«ãŒã決æžã®ãã£ã³ã»ã«ïŒè¿éããïŒ" , orderID: "order_card" , assertFunc: func (t *testing.T, orderID string ) { // 泚æã¹ããŒã¿ã¹ã®æ€èšŒ order, err := orderRepo.FindByID(ctx, orderID) AssertEqual(t, nil , err) AssertEqual(t, "CANCELLED" , order.Status) // è¿éããŒãã«ã®æ€èšŒ refund, err := refundRepo.FindByOrderID(ctx, orderID) AssertEqual(t, nil , err) AssertEqual(t, Refund{OrderID: orderID, amount: 1000 }, refund) }, }, { name: "å
šé¡ãã€ã³ãæãã®ãã£ã³ã»ã«ïŒãã€ã³ãéå
ããïŒ" , orderID: "order_point" , assertFunc: func (t *testing.T, orderID string ) { // 泚æã¹ããŒã¿ã¹ã®æ€èšŒ order, err := orderRepo.FindByID(ctx, orderID) AssertEqual(t, nil , err) AssertEqual(t, "CANCELLED" , order.Status) // ãŠãŒã¶ãŒãã€ã³ãã®æ€èšŒ user, err := userRepo.FindByID(ctx, order.UserID) AssertEqual(t, nil , err) AssertEqual(t, 1500 , user.Points) }, }, } for _, tt := range tests { t.Run(tt.name, func (t *testing.T) { // ãã¹ã察象ã®å®è¡ err := usecase.CancelOrder(ctx, tt.orderID) AssertEqual(t, nil , err) // ãã¹ãã±ãŒã¹åºæã®æ€èšŒåŠçãå®è¡ tt.assertFunc(t, tt.orderID) }) } // 差忀åºãçšããææ³ tests := [] struct { name string orderID string expectedDiff Diffs }{ { name: "ã¯ã¬ãžããã«ãŒã決æžã®ãã£ã³ã»ã«ïŒè¿éããïŒ" , orderID: "order_card" , expectedDiff: Diffs{ "orders" : { U: map [KeyHash]Record{ HashKey({ "id" : "order_card" }): { "status" : "CANCELLED" , }, }, }, "refunds" : { C: []Record{ { "order_id" : "order_card" , "amount" : 1000 , }, }, }, }, }, { name: "å
šé¡ãã€ã³ãæãã®ãã£ã³ã»ã«ïŒãã€ã³ãéå
ããïŒ" , orderID: "order_point" , expectedDiff: Diffs{ "orders" : { U: map [KeyHash]Record{ HashKey({ "id" : "order_point" }): { "status" : "CANCELLED" , }, }, }, "users" : { U: map [KeyHash]Record{ HashKey({ "id" : "user_1" }): { "points" : 1500 , }, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func (t *testing.T) { // å
šããŒãã«ã®å·®åããã£ããã£ãã€ã€å®è¡ var err error diffs := DiffDB(t.Context(), db, func () { err = usecase.CancelOrder(ctx, tt.orderID) }) // è¿ãå€ã®æ€èšŒ AssertEqual(t, nil , err) // å·®åã®æ€èšŒ AssertRecords(t, tt.expectedDiff, diffs) }) } å·®åæœåºã®å®è£
å·®åæœåºã¯ã以äžã®æé ã§è¡ããŸãã å
šããŒãã«ã®äž»ããŒæ
å ±ãå«ãã¹ããŒãæ
å ±ãååŸ ãã¹ã察象ã®é¢æ°å®è¡åã«ã察象ããŒã¿ããŒã¹ã®å
šããŒãã«ã®å
šã¬ã³ãŒããååŸããã¡ã¢ãªäžã«ä¿å ãã¹ã察象ã®é¢æ°ãå®è¡ 颿°å®è¡åŸã«ãå床å
šããŒãã«ã®å
šã¬ã³ãŒããååŸ å®è¡ååŸã®ã¬ã³ãŒããæ¯èŒããå·®åãæœåº ã¹ããŒãæ
å ±ã®ååŸãå
šã¬ã³ãŒãã®ååŸã¯ãããŒã¿ããŒã¹ã®çš®é¡ã«äŸåãããããDBSourceã€ã³ã¿ãã§ãŒã¹ãå®çŸ©ããåããŒã¿ããŒã¹ã«å¿ããå®è£
ãçšæããŸããã ããã§ã¯ãã¹ããŒãæ
å ±ãšå
šã¬ã³ãŒãã®ååŸã®å®è£
ã«ã€ããŠã¯å²æããå·®åæœåºã®ã³ã¢ããžãã¯ã瀺ããŸãã func createDiff(source DBSource, before, after map [ string ] map [KeyHash]Record) Diffs { diffs := Diffs{} // åããŒãã«ããšã«å·®åãæœåº for tableName := range before { diff := Diff{U: map [KeyHash]Record{}} // åããŒãã«ã®ã¹ããŒãæ
å ±ãååŸ schema := source.schemata()[tableName] // ã¬ã³ãŒãããšã«å·®åãæ¯èŒ // keyã¯åã¬ã³ãŒãã®äž»ããŒã®ããã·ã¥å€ for key, record := range before[tableName] { // å®è¡åã«ãã£ãã¬ã³ãŒããå®è¡åŸã«ãååšããå Žå if afterRecord, ok := after[tableName][key]; ok { updates := Record{} for k, v := range record { // å€ãç°ãªãã«ã©ã ã®ã¿ãæœåº if !reflect.DeepEqual(v, afterRecord[k]) { updates[k] = afterRecord[k] } } // æŽæ°ããã£ãå Žåã®ã¿diffã«è¿œå if len (updates) != 0 { diff.U[key] = updates } // Createãæœåºããããã«ãafterããæ¢åã¬ã³ãŒããåé€ delete (after[tableName], key) continue } keyValues := map [ string ]any{} for _, key := range schema.keys { keyValues[key] = record[key] } diff.D = append (diff.D, keyValues) } // æ®ã£ãafterã®ã¬ã³ãŒãã¯æ°èŠäœæãããã¬ã³ãŒã for _, record := range after[tableName] { diff.C = append (diff.C, record) } // æåŸ
å€ãæžãéã«å·®åããªãå Žåã¯çç¥å¯èœã«ããããã空ã¹ã©ã€ã¹ã¯nilã«å€æ if len (diff.U) == 0 { diff.U = nil } // ããŒãã«ã«äœããã®å·®åããã£ãå Žåã®ã¿diffsã«è¿œå if len (diff.C) != 0 || len (diff.U) != 0 || len (diff.D) != 0 { diffs[tableName] = diff } } // æåŸ
å€ãæžãéã«å·®åããªãå Žåã¯çç¥å¯èœã«ããããã空ã®Diffsã¯nilã«å€æ if len (diffs) == 0 { return nil } return diffs } è€æ°ããŒã¿ããŒã¹ãžã®å¯Ÿå¿ ZOZOTOWNã§ã¯ãªãã¬ã€ã¹ãé²ããã«ããããäžæçã«æ¢åç°å¢ãšæ°ç°å¢ããããã®ããŒã¿ããŒã¹ã«æžã蟌ãã±ãŒã¹ãååšããŸããããããã§æåŸ
ããå·®åãããããæ€èšŒã§ããããã«è€æ°ããŒã¿ããŒã¹ã«ã察å¿ããŸãããDiffExtractorã«ã©ãã«ãä»ããŠè€æ°èšå®ããããšã§ãå·®ååºåæã«ããããã©ã®ããŒã¿ããŒã¹ã§çããå·®åããå€å®ã§ããŸãã // è€æ°ããŒã¿ããŒã¹ã«å¯Ÿå¿ããå®è£
type DiffExtractor struct { // è€æ°ã®ããŒã¿ããŒã¹ãæœåºå¯Ÿè±¡ãšãã // mapã®ããŒã¯ããŒã¿ããŒã¹ãç¹å®ããããã®ã©ãã« sources map [ string ]DBSource } func NewDiffExtractor(sources map [ string ]DBSource) DiffExtractor { return DiffExtractor{sources: sources} } func (de DiffExtractor) Diff(ctx context.Context, f func ()) map [ string ]Diffs { diffs := map [ string ]Diffs{} before := map [ string ] map [ string ] map [KeyHash]Record{} for name, source := range de.sources { // ãã¹ã察象å®è¡åã®åããŒã¿ããŒã¹ããã£ãã㣠before[name] = source.dump(ctx) } f() after := map [ string ] map [ string ] map [KeyHash]Record{} for name, source := range de.sources { // ãã¹ã察象å®è¡åŸã®åããŒã¿ããŒã¹ããã£ãã㣠after[name] = source.dump(ctx) // åããŒã¿ããŒã¹ã®å·®åæœåº diffs[name] = createDiff(source, before[name], after[name]) } return diffs } // è€æ°ããŒã¿ããŒã¹ã§å©çšããå Žåã®ãã¹ããã«ããŒäŸ func DiffDBForDoubleWrite(ctx context.Context, mysqlDB *sql.DB, mssqlDB *sql.DB, f func ()) map [ string ]Diffs { mysqlSource := dd.NewMySQLSource(ctx, mysqlDB) mssqlSource := dd.NewMSSQLSource(ctx, mssqlDB) extractor := dd.NewDiffExtractor( map [ string ]dd.DBSource{ "mysql" : mysqlSource, "mssql" : mssqlSource, }) return extractor.Diff(ctx, func () { f() }) } å°å
¥æã®å·¥å€«ç¹ 差忀åºãå°å
¥ããã«ãããããã¹ãã®å®å®æ§ãä¿ã€ããã«ããã€ã工倫ããŸããã éåºå®å€ã®åãæ±ã æ¬ææ³ã§ã¯ãç¹å®ã®ã«ã©ã ã ãã§ãªãã¬ã³ãŒãå
šäœã察象ãšãããããèªåæ¡çªãããIDãçŸåšæå»ãä¹±æ°ãªã©å®è¡ã®ãã³ã«å€ãå€ããã«ã©ã ã«ã€ããŠãåžžã«èæ
®ããå¿
èŠããããŸãã IDã®èªåæ¡çªã«ã€ããŠã¯ãåãã¹ãã±ãŒã¹ã®å®è¡åã«ãªãŒãã€ã³ã¯ãªã¡ã³ããªã©ã®ã·ãŒã±ã³ã¹ããªã»ããããããã«ãTRUNCATEæãå®è¡ããããšã§å¯Ÿå¿ããŸãããããã«ãããçºè¡ãããIDãåºå®ããæåŸ
å€ãåºå®ã§ããŸãã // ãã¹ãå®è¡åã®ã»ããã¢ããäŸ func SetupTestDB(t *testing.T, db *sql.DB) { t.Helper() // ãŠãŒã¶ãŒå®çŸ©ãããå
šããŒãã«åã®ååŸ tables := GetTableNames(db) for _, table := range tables { _, err := db.Exec(fmt.Sprintf( "TRUNCATE TABLE %s" , table)) if err != nil { t.Fatal(err) } } } å®éã«TRUNCATEæãå®è¡ããã«ã¯å€éšããŒã®å¶çŽãã§ãã¯ãäžæçã«è§£é€ããããããã¯ããŒãã«ã®åŠçé åºãå¶åŸ¡ãããšãã£ãããšãå¿
èŠã«ãªããŸãã çŸåšæå»ã«ã€ããŠã¯ã颿°å
ã§ time.Now() ã䜿ãããæå»ãåŒæ°ãšããŠæž¡ãããã€ã³ã¿ãã§ãŒã¹ãä»ããŠæ³šå
¥ããããšã§ãã¹ãå
ã®æå»ãåºå®ããŠããŸããããã«ãããæå»ã«é¢ããæåŸ
å€ãåºå®ã§ããŸãã ä¹±æ°ã«ã€ããŠã¯ãä¹±æ°çæã®ç®æãã€ã³ã¿ãã§ãŒã¹åããŠæåŸ
å€ãåºå®ããæ¹æ³ãªã©ãèããããŸããããããé£ããå Žåãèæ
®ããŠãã¢ãµãŒã·ã§ã³é¢æ°ã«ãããŠå€äžèŽä»¥å€ãå¯èœã«ããŸãããå
·äœçã«ã¯æååã«å¯ŸããæåŸ
å€ã« *regexp.Regexp ãæå®ããå Žåã«ã¯æ£èŠè¡šçŸããããè¡ãããã«ããŸããã // ä¹±æ°ãå«ããã£ãŒã«ãã®æ€èšŒäŸ expectedDiffs := Diffs{ "orders" : { C: []Record{ { "order_id" : regexp.MustCompile( `\A[0-9a-f]{32}\z` ), // ä¹±æ°ãæ£èŠè¡šçŸã§è¡šçŸ "amount" : 10 , "order_at" : "2026-01-01T00:00:00Z" , }, }, }, } AssertRecords(t, expectedDiffs, actualDiffs) çŸç¶ã¯äœ¿ãã±ãŒã¹ããªãã£ãããçšæããŠããŸããããæ°å€åã®ä¹±æ°ãå©çšããå Žåã«ã¯ããããå°çšã®åãçšæããŠãæ€èšŒåŠçãåãæ¿ããããšãæ€èšããŠããŸãã æåŸ
å€ã®æ£èŠå äŒå¡åºç€ã§ã¯ãã¹ãããŒã¿ã®ã»ããã¢ããã«ã¬ã³ãŒãããŒã¿ã§ã¯ãªãã¢ãã«ããŒã¿ãå©çšããŠãããããããŒã¿ããŒã¹ããæœåºããå·®åã®å€ãšãã¹ãã±ãŒã¹ã®æåŸ
å€ãšã§ã¯åœ¢åŒãç°ãªãããšããããŸããäŸãã°ãã¢ãã«ããŒã¿ã§ã¯boolåã®ãã£ãŒã«ãããããŒã¿ããŒã¹ããã®åºåæã¯intåã®0ãããã¯1ã«ãªãã±ãŒã¹ããããŸããä»ã«ãã¢ãã«ããŒã¿ã§ã¯å€ãªããžã§ã¯ããšããŠå®çŸ©ãããŠãããã£ãŒã«ãããããŒã¿ããŒã¹ããã®åºåæã¯ãã®å€ãªããžã§ã¯ãã®å
éšã®å€ã«ãªãã±ãŒã¹ããããŸãã ãã®ãããªå Žåã«ããã¹ãã±ãŒã¹ã®æåŸ
å€ãããŒã¿ããŒã¹ããã®åºåã«åããã圢åŒã§èšè¿°ããã®ã¯ããã¹ãã±ãŒã¹ã®å¯èªæ§ãæãªããããã¢ãµãŒã·ã§ã³é¢æ°å
ã§æ¯èŒæã«æ£èŠåããæ¹éãšããŸãããå®è£
ã®è©³çްã¯å²æããŸããããªãã¬ã¯ã·ã§ã³ãçšã㊠reflect.ValueOf 颿°ã§ reflect.Value ã«å€æããåŸã Kind() ã¡ãœããã§å
ãšãªãåãå€å®ããŠæ£èŠåãè¡ã£ãŠããŸãã å·®åã®é€å€ ããŒã¿ããŒã¹ã®ããªã¬ãŒåŠçã«ããæå»ã®æ¿å
¥ãªã©ã¢ããªã±ãŒã·ã§ã³åŽããå¶åŸ¡ã§ããªãå€ããããã¢ããªã±ãŒã·ã§ã³äžããå©çšããŠããªãã«ã©ã ã®ãããªäŸå€çã«å·®åããé€å€ãããã±ãŒã¹ãååšããŸããããã§ãæœåºããå·®åããã«ã©ã ãæå®ããŠé€å€ããããã® Ignore() ã¡ãœãããçšæããŸããããŸããçšæãããŠããªãæ¹æ³ã§ç¹å®ã®ã«ã©ã ãæ€èšŒããããã«äžæŠãå·®åããåãé€ããäžã§å¥éæ€èšŒãããšããå Žåã«ãå©çšã§ããŸãã diffs := DiffDB(ctx, db, func () { someFunc() }) // hogeã¯å»æ¢æžã¿ã®ã«ã©ã ã§æåŸ
å€ã®ç®¡ç察象å€ãšãã AssertRecords(t, expectedDiffs, diffs.Ignore( "hoge" )) ãŸããç¹å®ã®ãã¹ãã±ãŒã¹ã«ããããã¢ããªã±ãŒã·ã§ã³å
šäœã§é€å€ãããæ¡ä»¶ããããããªå Žåã«å¯Ÿå¿ãããããDiffExtractorã«é€å€çšã®é¢æ°ããªãã·ã§ã³ã§èšå®ã§ããããã«ããŸããã var someIgnoreColumnFunc = func (tableName, columnName string ) bool { // äŸãã°ãå
šããŒãã«ã®hogeã«ã©ã ãåžžã«é€å€ããå Žå return columnName == "hoge" } // é€å€é¢æ°ããªãã·ã§ã³ã«èšå®ãããã¹ããã«ããŒäŸ func DiffDB(ctx context.Context, db *sql.DB, f func ()) dd.Diffs { source := dd.NewMySQLSource(ctx, db) source.WithIgnoreColumnFunc(someIgnoreColumnFunc) extractor := dd.NewDiffExtractor( map [ string ]dd.DBSource{ "mysql" : source, }) diffs := extractor.Diff(ctx, func () { f() }) return diffs[ "mysql" ] } ãŸãšã æ¬èšäºã§ã¯ãGoãçšããããŒã¿ããŒã¹ã®ã¬ã³ãŒã差忀åºã«ãããã¹ãææ³ã«ã€ããŠç޹ä»ããŸããã è€éãªãã¹ãã±ãŒã¹ã«ãªãã»ã©ãããŒã¿ããŒã¹ãžã®å¯äœçšãç¶²çŸ
çã«æ€èšŒããããšã®éèŠæ§ãå¢ããŸããæ¬ææ³ãå°å
¥ããããšã§ãæåŸ
ããªãå¯äœçšãæ€ç¥ãããããªãããã¹ãã³ãŒãã®å¯èªæ§ãšä¿å®æ§ãåäžããŸãããä»åŸããããè¯ããã¹ãææ³ã®æš¡çŽ¢ãšæ¹åãç¶ããŠãããããšæããŸãã ZOZOã§ã¯ãäžç·ã«ãµãŒãã¹ãäœãäžããŠãããæ¹ãåéäžã§ãããèå³ã®ããæ¹ã¯ã以äžã®ãªã³ã¯ãããã²ãå¿åãã ããã hrmos.co