ฟังก์ชั่นเยาะเย้ยในไป


147

ฉันกำลังเรียนรู้ไปด้วยการเข้ารหัสโปรเจ็คต์ส่วนตัวขนาดเล็ก แม้ว่ามันจะเล็ก แต่ฉันก็ตัดสินใจทำการทดสอบหน่วยอย่างเข้มงวดเพื่อเรียนรู้นิสัยที่ดีใน Go ตั้งแต่เริ่มต้น

การทดสอบหน่วยเล็กน้อยได้ทั้งหมดดีและโอชะ แต่ฉันงงกับการอ้างอิงในขณะนี้ ฉันต้องการที่จะสามารถแทนที่การเรียกฟังก์ชั่นบางอย่างด้วยการจำลอง นี่คือตัวอย่างรหัสของฉัน:

func get_page(url string) string {
    get_dl_slot(url)
    defer free_dl_slot(url)

    resp, err := http.Get(url)
    if err != nil { return "" }
    defer resp.Body.Close()

    contents, err := ioutil.ReadAll(resp.Body)
    if err != nil { return "" }
    return string(contents)
}

func downloader() {
    dl_slots = make(chan bool, DL_SLOT_AMOUNT) // Init the download slot semaphore
    content := get_page(BASE_URL)
    links_regexp := regexp.MustCompile(LIST_LINK_REGEXP)
    matches := links_regexp.FindAllStringSubmatch(content, -1)
    for _, match := range matches{
        go serie_dl(match[1], match[2])
    }
}

ฉันต้องการที่จะทดสอบ downloader () โดยไม่ได้รับหน้าผ่าน http - เช่นโดยการเยาะเย้ย get_page (ง่ายกว่าเพราะมันจะคืนค่าเนื้อหาของหน้าเป็นสตริง) หรือ http.Get ()

ฉันพบกระทู้นี้: https://groups.google.com/forum/#!topic/golang-nuts/6AN1E2CJOxIซึ่งดูเหมือนว่าจะมีปัญหาที่คล้ายกัน Julian Phillips นำเสนอห้องสมุด Withmock ( http://github.com/qur/withmock ) เป็นวิธีแก้ปัญหา แต่ฉันไม่สามารถใช้งานได้ นี่คือส่วนที่เกี่ยวข้องของรหัสการทดสอบของฉันซึ่งส่วนใหญ่เป็นรหัสลัทธิขนส่งสินค้าให้ฉันตามจริง:

import (
    "testing"
    "net/http" // mock
    "code.google.com/p/gomock"
)
...
func TestDownloader (t *testing.T) {
    ctrl := gomock.NewController()
    defer ctrl.Finish()
    http.MOCK().SetController(ctrl)
    http.EXPECT().Get(BASE_URL)
    downloader()
    // The rest to be written
}

ผลการทดสอบมีดังต่อไปนี้:

ERROR: Failed to install '_et/http': exit status 1
output:
can't load package: package _et/http: found packages http (chunked.go) and main (main_mock.go) in /var/folders/z9/ql_yn5h550s6shtb9c5sggj40000gn/T/withmock570825607/path/src/_et/http

Withmock เป็นทางออกสำหรับปัญหาการทดสอบของฉันหรือไม่? ฉันควรทำอย่างไรเพื่อให้มันใช้งานได้


เนื่องจากคุณกำลังดำน้ำในการทดสอบหน่วย Go ดูที่GoConveyสำหรับวิธีที่ยอดเยี่ยมในการทำการทดสอบตามพฤติกรรม ... และทีเซอร์: เว็บ UI ที่อัปเดตโดยอัตโนมัติกำลังมาซึ่งยังใช้งานได้กับการทดสอบ "go test" ดั้งเดิม
แมตต์

คำตอบ:


193

ขอชื่นชมคุณสำหรับการฝึกการทดสอบที่ดี! :)

โดยส่วนตัวแล้วฉันไม่ได้ใช้gomock(หรือกรอบการเยาะเย้ยใด ๆ สำหรับเรื่องนั้นการเยาะเย้ยใน Go เป็นเรื่องง่ายมากหากไม่มี) ฉันจะผ่านการพึ่งพาdownloader()ฟังก์ชั่นเป็นพารามิเตอร์หรือฉันจะทำให้downloader()วิธีการในประเภทและประเภทที่สามารถถือการget_pageพึ่งพา:

วิธีที่ 1: ส่งผ่านget_page()เป็นพารามิเตอร์ของdownloader()

type PageGetter func(url string) string

func downloader(pageGetterFunc PageGetter) {
    // ...
    content := pageGetterFunc(BASE_URL)
    // ...
}

หลัก:

func get_page(url string) string { /* ... */ }

func main() {
    downloader(get_page)
}

ทดสอบ:

func mock_get_page(url string) string {
    // mock your 'get_page()' function here
}

func TestDownloader(t *testing.T) {
    downloader(mock_get_page)
}

วิธีที่ 2: สร้างdownload()วิธีการชนิดDownloader:

หากคุณไม่ต้องการผ่านการพึ่งพาเป็นพารามิเตอร์คุณสามารถสร้างget_page()สมาชิกประเภทและสร้างdownload()วิธีการประเภทนั้นซึ่งสามารถใช้get_page:

type PageGetter func(url string) string

type Downloader struct {
    get_page PageGetter
}

func NewDownloader(pg PageGetter) *Downloader {
    return &Downloader{get_page: pg}
}

func (d *Downloader) download() {
    //...
    content := d.get_page(BASE_URL)
    //...
}

หลัก:

func get_page(url string) string { /* ... */ }

func main() {
    d := NewDownloader(get_page)
    d.download()
}

ทดสอบ:

func mock_get_page(url string) string {
    // mock your 'get_page()' function here
}

func TestDownloader() {
    d := NewDownloader(mock_get_page)
    d.download()
}

4
ขอบคุณมาก! ฉันไปกับคนที่สอง (มีบางฟังก์ชั่นอื่น ๆ ด้วยเช่นกันที่ฉันต้องการจะเยาะเย้ย ฉันรัก Go เล็กน้อย โดยเฉพาะอย่างยิ่งคุณสมบัติการทำงานพร้อมกันนั้นเรียบร้อย!
GolDDranks

149
ฉันเป็นคนเดียวที่ค้นพบว่าเพื่อประโยชน์ในการทดสอบเราต้องเปลี่ยนรหัสหลัก / ฟังก์ชั่นลายเซ็นนั้นแย่มาก?
โทมัส

41
@ โทมัสฉันไม่แน่ใจว่าคุณเป็นคนเดียวหรือไม่ แต่เป็นเหตุผลพื้นฐานสำหรับการพัฒนาแบบทดสอบ - การทดสอบของคุณจะเป็นแนวทางในการเขียนโค้ดการผลิตของคุณ รหัสที่ทดสอบได้นั้นเป็นแบบแยกส่วน ในกรณีนี้พฤติกรรมของ 'get_page' ของออบเจ็กต์จะสามารถทำการเชื่อมต่อได้ - เราสามารถเปลี่ยนการใช้งานแบบไดนามิกได้ คุณต้องเปลี่ยนรหัสหลักของคุณหากมันถูกเขียนไม่ดีในตอนแรก
weberc2

21
@Thomas ฉันไม่เข้าใจประโยคที่สองของคุณ TDD ขับรหัสที่ดีกว่า รหัสของคุณเปลี่ยนแปลงเพื่อให้สามารถทดสอบได้ (เนื่องจากรหัสที่ทดสอบได้นั้นเป็นโมดูลที่จำเป็นต้องมีอินเทอร์เฟซที่ใช้ความคิดดี) แต่จุดประสงค์หลักคือการมีรหัสที่ดีกว่าการมีการทดสอบอัตโนมัติ หากความกังวลของคุณคือการเปลี่ยนรหัสการทำงานเพียงเพื่อเพิ่มการทดสอบหลังจากความจริงฉันยังคงแนะนำให้เปลี่ยนเพราะเพียงแค่มีความเป็นไปได้ที่ใครสักคนจะต้องการอ่านรหัสนั้นหรือเปลี่ยนแปลงมัน
weberc2

6
@ แน่นอนถ้าคุณเขียนข้อสอบในขณะที่ไปคุณจะไม่ต้องรับมือกับปริศนาดังกล่าว
weberc2

24

หากคุณเปลี่ยนนิยามฟังก์ชันของคุณเพื่อใช้ตัวแปรแทน:

var get_page = func(url string) string {
    ...
}

คุณสามารถแทนที่มันในการทดสอบของคุณ:

func TestDownloader(t *testing.T) {
    get_page = func(url string) string {
        if url != "expected" {
            t.Fatal("good message")
        }
        return "something"
    }
    downloader()
}

แต่ระวังการทดสอบอื่น ๆ ของคุณอาจล้มเหลวหากพวกเขาทดสอบการทำงานของฟังก์ชั่นที่คุณแทนที่!

ผู้เขียน Go ใช้รูปแบบนี้ในไลบรารีมาตรฐาน Go เพื่อแทรก hooks การทดสอบลงในโค้ดเพื่อให้ง่ายต่อการทดสอบ:

https://golang.org/src/net/hook.go

https://golang.org/src/net/dial.go#L248

https://golang.org/src/net/dial_test.go#L701


8
ลงคะแนนหากคุณต้องการนี่เป็นรูปแบบที่ยอมรับได้สำหรับแพ็คเกจขนาดเล็กเพื่อหลีกเลี่ยงการสร้างแผ่นที่เกี่ยวข้องกับ DI ตัวแปรที่มีฟังก์ชั่นเป็นเพียง "ทั่วโลก" ในขอบเขตของแพคเกจเนื่องจากมันไม่ได้ถูกส่งออก นี่คือตัวเลือกที่ถูกต้องฉันพูดถึงข้อเสียเลือกการผจญภัยของคุณเอง
Jake

4
สิ่งหนึ่งที่ควรทราบคือฟังก์ชันที่กำหนดด้วยวิธีนี้ไม่สามารถเรียกซ้ำได้
Ben Sandler

2
ฉันเห็นด้วยกับ @ Jake ว่าวิธีการนี้มีที่อยู่
m.kocikowski

11

ฉันใช้วิธีที่แตกต่างกันเล็กน้อยที่วิธีการโครงสร้างสาธารณะใช้อินเตอร์เฟสแต่ตรรกะของพวกเขาถูก จำกัด เพียงแค่ห่อฟังก์ชั่นส่วนตัว (ไม่ได้ส่งออก) ซึ่งใช้อินเทอร์เฟซเหล่านั้นเป็นพารามิเตอร์ สิ่งนี้จะช่วยให้คุณมีความละเอียดที่คุณจะต้องล้อเลียนการพึ่งพาใด ๆ และยังมี API แบบสะอาดที่จะใช้จากนอกชุดทดสอบของคุณ

เพื่อให้เข้าใจสิ่งนี้มีความจำเป็นที่จะต้องเข้าใจว่าคุณสามารถเข้าถึงวิธีที่ไม่ได้ส่งออกในกรณีทดสอบของคุณ (เช่นจากใน_test.goไฟล์ของคุณ) ดังนั้นคุณจึงทดสอบสิ่งเหล่านั้นแทนการทดสอบสิ่งที่ส่งออกซึ่งไม่มีตรรกะ

เพื่อสรุป: ทดสอบฟังก์ชั่นที่ไม่ได้เอ็กซ์พอร์ตแทนการทดสอบฟังก์ชั่นส่งออก!

ลองทำตัวอย่าง บอกว่าเรามีโครงสร้าง Slack API ซึ่งมีสองวิธี:

  • SendMessageวิธีการซึ่งจะส่งคำขอ HTTP ไป webhook หย่อน
  • SendDataSynchronouslyวิธีการซึ่งได้รับชิ้นของสตริง iterates กว่าพวกเขาและเรียกร้องให้SendMessageทุกซ้ำ

ดังนั้นเพื่อที่จะทดสอบSendDataSynchronouslyโดยไม่ทำการร้องขอ HTTP ทุกครั้งที่เราต้องล้อเล่นSendMessageใช่ไหม

package main

import (
    "fmt"
)

// URI interface
type URI interface {
    GetURL() string
}

// MessageSender interface
type MessageSender interface {
    SendMessage(message string) error
}

// This one is the "object" that our users will call to use this package functionalities
type API struct {
    baseURL  string
    endpoint string
}

// Here we make API implement implicitly the URI interface
func (api *API) GetURL() string {
    return api.baseURL + api.endpoint
}

// Here we make API implement implicitly the MessageSender interface
// Again we're just WRAPPING the sendMessage function here, nothing fancy 
func (api *API) SendMessage(message string) error {
    return sendMessage(api, message)
}

// We want to test this method but it calls SendMessage which makes a real HTTP request!
// Again we're just WRAPPING the sendDataSynchronously function here, nothing fancy
func (api *API) SendDataSynchronously(data []string) error {
    return sendDataSynchronously(api, data)
}

// this would make a real HTTP request
func sendMessage(uri URI, message string) error {
    fmt.Println("This function won't get called because we will mock it")
    return nil
}

// this is the function we want to test :)
func sendDataSynchronously(sender MessageSender, data []string) error {
    for _, text := range data {
        err := sender.SendMessage(text)

        if err != nil {
            return err
        }
    }

    return nil
}

// TEST CASE BELOW

// Here's our mock which just contains some variables that will be filled for running assertions on them later on
type mockedSender struct {
    err      error
    messages []string
}

// We make our mock implement the MessageSender interface so we can test sendDataSynchronously
func (sender *mockedSender) SendMessage(message string) error {
    // let's store all received messages for later assertions
    sender.messages = append(sender.messages, message)

    return sender.err // return error for later assertions
}

func TestSendsAllMessagesSynchronously() {
    mockedMessages := make([]string, 0)
    sender := mockedSender{nil, mockedMessages}

    messagesToSend := []string{"one", "two", "three"}
    err := sendDataSynchronously(&sender, messagesToSend)

    if err == nil {
        fmt.Println("All good here we expect the error to be nil:", err)
    }

    expectedMessages := fmt.Sprintf("%v", messagesToSend)
    actualMessages := fmt.Sprintf("%v", sender.messages)

    if expectedMessages == actualMessages {
        fmt.Println("Actual messages are as expected:", actualMessages)
    }
}

func main() {
    TestSendsAllMessagesSynchronously()
}

สิ่งที่ฉันชอบเกี่ยวกับวิธีการนี้คือการดูวิธีการที่ไม่ได้ส่งออกคุณสามารถเห็นได้อย่างชัดเจนว่าการพึ่งพาคืออะไร ในเวลาเดียวกัน API ที่คุณส่งออกนั้นสะอาดกว่ามากและมีพารามิเตอร์น้อยกว่าให้ผ่านเนื่องจากการพึ่งพาที่แท้จริงที่นี่เป็นเพียงตัวรับพาเรนต์ซึ่งใช้งานอินเทอร์เฟซเหล่านั้นทั้งหมด แต่ฟังก์ชั่นทุกอย่างอาจขึ้นอยู่กับเพียงส่วนหนึ่งของมัน (หนึ่งหรือสองอินเตอร์เฟส) ซึ่งทำให้ refactors ง่ายขึ้นมาก เป็นเรื่องดีที่ได้เห็นว่าโค้ดของคุณเชื่อมโยงกันอย่างไรโดยดูจากฟังก์ชั่นของลายเซ็นต์ฉันคิดว่ามันเป็นเครื่องมือที่ทรงพลังในการป้องกันการดมกลิ่นโค้ด

ที่จะทำให้สิ่งที่ง่ายฉันใส่ทุกอย่างลงในไฟล์เดียวที่จะช่วยให้คุณสามารถเรียกใช้รหัสในที่สนามเด็กเล่นที่นี่แต่ผมขอแนะนำให้คุณตรวจสอบออกตัวอย่างเต็มรูปแบบใน GitHub นี่คือslack.goไฟล์และนี่slack_test.go

และนี่คือสิ่งทั้งหมด:)


นี่เป็นวิธีการที่น่าสนใจและการรู้วิธีการเข้าถึงวิธีการส่วนตัวในไฟล์ทดสอบนั้นมีประโยชน์จริง ๆ มันทำให้ฉันนึกถึงเทคนิค pimpl ใน C ++ อย่างไรก็ตามฉันคิดว่าควรจะกล่าวว่าการทดสอบฟังก์ชั่นส่วนตัวนั้นอันตราย สมาชิกส่วนบุคคลมักจะพิจารณารายละเอียดการใช้งานและมีแนวโน้มที่จะเปลี่ยนแปลงตลอดเวลากว่าส่วนต่อประสานสาธารณะ ตราบใดที่คุณทดสอบ wrappers ส่วนตัวรอบ ๆ ส่วนต่อประสานสาธารณะ แต่คุณก็ควรจะปรับ
c1moore

ใช่โดยทั่วไปฉันจะเห็นด้วยกับคุณ ในกรณีนี้แม้ว่าวิธีการส่วนตัวจะเหมือนกันกับสาธารณะดังนั้นคุณจะต้องทำการทดสอบในสิ่งเดียวกัน ข้อแตกต่างระหว่างสองอย่างนี้คืออาร์กิวเมนต์ของฟังก์ชัน นั่นเป็นเคล็ดลับที่ช่วยให้คุณสามารถฉีดการพึ่งพาใด ๆ (เยาะเย้ยหรือไม่) ตามต้องการ
Francesco Casula

ใช่ฉันเห็นด้วย ฉันแค่บอกว่าตราบใดที่คุณ จำกัด ให้วิธีการส่วนตัวที่ห่อคนเหล่านั้นคุณควรจะดีไป อย่าเพิ่งเริ่มทดสอบวิธีการส่วนตัวที่เป็นรายละเอียดการใช้งาน
c1moore

7

ฉันจะทำสิ่งที่ชอบ

หลัก

var getPage = get_page
func get_page (...

func downloader() {
    dl_slots = make(chan bool, DL_SLOT_AMOUNT) // Init the download slot semaphore
    content := getPage(BASE_URL)
    links_regexp := regexp.MustCompile(LIST_LINK_REGEXP)
    matches := links_regexp.FindAllStringSubmatch(content, -1)
    for _, match := range matches{
        go serie_dl(match[1], match[2])
    }
}

ทดสอบ

func TestDownloader (t *testing.T) {
    origGetPage := getPage
    getPage = mock_get_page
    defer func() {getPage = origGatePage}()
    // The rest to be written
}

// define mock_get_page and rest of the codes
func mock_get_page (....

และฉันจะหลีกเลี่ยง_ใน golang ควรใช้ camelCase


1
เป็นไปได้ไหมที่จะพัฒนาแพคเกจที่สามารถทำได้สำหรับคุณ p := patch(mockGetPage, getPage); defer p.done()ฉันคิดว่าสิ่งที่ชอบ: ฉันใหม่ไปและพยายามทำสิ่งนี้โดยใช้unsafeห้องสมุด แต่ดูเหมือนว่าเป็นไปไม่ได้ที่จะทำในกรณีทั่วไป
vitiral

@ ลดลงนี่เป็นคำตอบที่ฉันเขียนในช่วงเวลาหนึ่งปีหลังจากฉัน
Jake

1
1. ความคล้ายคลึงกันเท่านั้นคือวิธี var ส่วนกลาง @ เจค 2. ความเรียบง่ายดีกว่าความซับซ้อน weberc2
ลดลง

1
@ fallen ฉันไม่คิดว่าตัวอย่างของคุณจะง่ายกว่านี้ การส่งผ่านข้อโต้แย้งนั้นไม่ซับซ้อนกว่าการกลายพันธุ์ในระดับโลก แต่การพึ่งพาอาศัยในระดับโลกทำให้เกิดปัญหามากมายที่ไม่มีอยู่จริง ตัวอย่างเช่นคุณจะต้องจัดการกับสภาพการแข่งขันหากคุณต้องการทดสอบแบบขนานของคุณ
weberc2

มันเกือบจะเหมือนกัน แต่ไม่ใช่ :) ในคำตอบนี้ฉันเห็นวิธีการกำหนดฟังก์ชั่นให้กับ var และวิธีการนี้ทำให้ฉันสามารถกำหนดการใช้งานที่แตกต่างกันสำหรับการทดสอบ ฉันไม่สามารถเปลี่ยนข้อโต้แย้งของฟังก์ชั่นที่ฉันกำลังทดสอบได้นี่เป็นทางออกที่ดีสำหรับฉัน อีกทางเลือกหนึ่งคือใช้ตัวรับกับโครงสร้างจำลองฉันไม่รู้ว่าอันไหนง่ายกว่า
alexbt

0

คำเตือน: สิ่งนี้อาจขยายขนาดไฟล์ที่เรียกใช้งานได้เล็กน้อยและเสียค่าใช้จ่ายเล็กน้อย IMO จะดีกว่าถ้า golang มีคุณสมบัติเช่นแมโครหรือมัณฑนากรฟังก์ชั่น

หากคุณต้องการเยาะเย้ยฟังก์ชั่นโดยไม่เปลี่ยน API วิธีที่ง่ายที่สุดคือเปลี่ยนการใช้งานเล็กน้อย:

func getPage(url string) string {
  if GetPageMock != nil {
    return GetPageMock()
  }

  // getPage real implementation goes here!
}

func downloader() {
  if GetPageMock != nil {
    return GetPageMock()
  }

  // getPage real implementation goes here!
}

var GetPageMock func(url string) string = nil
var DownloaderMock func() = nil

วิธีนี้เราสามารถเยาะเย้ยฟังก์ชั่นหนึ่งจากฟังก์ชั่นอื่น ๆ เพื่อความสะดวกมากยิ่งขึ้นเราสามารถให้แผ่นฉนวนกันความร้อนที่เยาะเย้ย:

// download.go
func getPage(url string) string {
  if m.GetPageMock != nil {
    return m.GetPageMock()
  }

  // getPage real implementation goes here!
}

func downloader() {
  if m.GetPageMock != nil {
    return m.GetPageMock()
  }

  // getPage real implementation goes here!
}

type MockHandler struct {
  GetPage func(url string) string
  Downloader func()
}

var m *MockHandler = new(MockHandler)

func Mock(handler *MockHandler) {
  m = handler
}

ในไฟล์ทดสอบ:

// download_test.go
func GetPageMock(url string) string {
  // ...
}

func TestDownloader(t *testing.T) {
  Mock(&MockHandler{
    GetPage: GetPageMock,
  })

  // Test implementation goes here!

  Mock(new(MockHandler)) // Reset mocked functions
}

-2

พิจารณาการทดสอบหน่วยเป็นโดเมนของคำถามนี้ขอแนะนำให้คุณใช้https://github.com/bouk/monkey แพคเกจนี้ทำให้คุณทดสอบการเยาะเย้ยโดยไม่ต้องเปลี่ยนรหัสต้นฉบับของคุณ เปรียบเทียบกับคำตอบอื่น ๆ มันมากกว่า "ไม่ล่วงล้ำ" 。

หลัก

type AA struct {
 //...
}
func (a *AA) OriginalFunc() {
//...
}

การทดสอบก่อนวันจริง

var a *AA

func NewFunc(a *AA) {
 //...
}

monkey.PatchMethod(reflect.TypeOf(a), "OriginalFunc", NewFunc)

ด้านที่ไม่ดีคือ:

- เตือนโดย Dave.C วิธีนี้ไม่ปลอดภัย ดังนั้นอย่าใช้นอกการทดสอบหน่วย

- เป็นไปไม่ใช่สำนวน

ด้านดีคือ:

++ ไม่ล่วงล้ำ ทำให้คุณทำสิ่งต่าง ๆ โดยไม่ต้องเปลี่ยนรหัสหลัก อย่างที่โธมัสพูด

++ ทำให้คุณเปลี่ยนพฤติกรรมของแพ็คเกจ (อาจได้รับจากบุคคลที่สาม) ด้วยโค้ดน้อยที่สุด


1
โปรดอย่าทำเช่นนี้ มันไม่ปลอดภัยอย่างสมบูรณ์และสามารถทำลาย internals ที่หลากหลาย ไม่ต้องพูดถึงมันไม่ได้ใช้สำนวนทางไกล
เดฟ C

1
@DaveC ฉันเคารพประสบการณ์ของคุณเกี่ยวกับ Golang แต่สงสัยว่าความคิดเห็นของคุณ 1. ความปลอดภัยไม่ได้หมายถึงทั้งหมดสำหรับการพัฒนาซอฟต์แวร์ทำคุณสมบัติที่หลากหลายและสะดวกสบาย 2. Idiomatic Golang ไม่ใช่ Golang เป็นส่วนหนึ่งของมัน ถ้าโครงการหนึ่งเป็นโอเพ่นซอร์สมันเป็นเรื่องปกติที่คนอื่นจะเล่นสกปรก ชุมชนควรสนับสนุนอย่างน้อยก็อย่าระงับ
แฟรงก์หวังส

2
ภาษาที่เรียกว่าไป โดยไม่ปลอดภัยฉันหมายความว่ามันสามารถทำลายรันไทม์ไปสิ่งต่าง ๆ เช่นการเก็บขยะ
เดฟ C

1
สำหรับฉันแล้วไม่ปลอดภัยจะเย็นสำหรับการทดสอบหน่วย หากจำเป็นต้องมีการปรับเปลี่ยนรหัสด้วยส่วนต่อประสานเพิ่มเติมทุกครั้งที่ทำการทดสอบหน่วย มันเหมาะกับฉันมากกว่าที่ใช้วิธีที่ไม่ปลอดภัยในการแก้ปัญหา
แฟรงค์วัง

1
@DaveC ฉันเห็นด้วยอย่างยิ่งว่านี่เป็นความคิดที่น่ากลัว (คำตอบของฉันคือคำตอบที่ได้รับการโหวตสูงสุดและเป็นที่ยอมรับ) แต่จะพูดจาหยาบคายฉันไม่คิดว่ามันจะทำลาย GC เพราะ Go GC นั้นอนุรักษ์นิยมและตั้งใจจัดการกรณีเช่นนี้ อย่างไรก็ตามฉันมีความสุขที่ได้รับการแก้ไขอย่างไรก็ตาม
weberc2
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.