It took me about 20 minutes to know the way to access a property in a struct that is passed by a channel in a unit test. I knew it in a blocking way but didn’t know in a non-blocking way. So let’s learn here how to do it.
Let’s catch up on the basics first with a literal value. And then, look at the code for struct.
How to get channel data and compare the literal value
A channel can be used in the following way in a unit test.
It("Expect", func() {
eventChan := make(chan int, 10)
go func(ch chan int) {
ch <- 22
}(eventChan)
Expect(<-eventChan).To(Equal(22))
})
However, this way completely blocks the thread. The test doesn’t finish if it’s not implemented properly.
Non blocking way to get channel data
It’s better not to block the thread in a unit test. It can be done with Eventually
instead of Expect
.
It("Receive", func() {
eventChan := make(chan int, 10)
go func(ch chan int) {
ch <- 22
}(eventChan)
Eventually(eventChan).Should(Receive())
})
We can set a timeout by WithTimeout
if necessary. The combination of Eventually
and Receive
improves the situation above but this test doesn’t check the value itself. We need an additional statement for the value comparison.
Receive
function accepts as many gomega matchers as we need. In this case, we can put Equal
in Receive
function.
It("Receive with Equal", func() {
eventChan := make(chan int, 10)
go func(ch chan int) {
ch <- 22
}(eventChan)
Eventually(eventChan).Should(Receive(Equal(22)))
})
The property can’t be read in Receive function
Let’s write a test for struct. The struct is simple enough. The test can be written in the same way as literal.
type Event struct {
Name string
Value string
}
It("Receive with Equal", func() {
eventChan := make(chan Event, 10)
go func(ch chan Event) {
ch <- Event{
Name: "Running",
Value: "dummy data",
}
}(eventChan)
Eventually(eventChan).Should(Receive(Equal(Event{
Name: "Running",
Value: "dummy data",
})))
})
As you can see above, Equal
function can be used here. However, if the struct has a pointer property or struct in it, the comparison result becomes false. A property in the struct can’t be read directly in this way. If we use Expect
here, it will be a blocking call.
Pass the channel data to another variable
I found an example on the official page.
https://onsi.github.io/gomega/#receivevar receivedBagel Bagel Eventually(bagelChan).Should(Receive(&receivedBagel)) Ω(receivedBagel.Contents()).Should(ContainElement("cream cheese")) Ω(receivedBagel.Kind()).Should(Equal("sesame"))
It seems to be possible to get the data by passing an address to Receive
function.
It("Receive and get struct data", func() {
eventChan := make(chan Event, 10)
go func(ch chan Event) {
ch <- Event{
Name: "Running",
Value: "dummy data",
}
}(eventChan)
var receivedEvent Event
Eventually(eventChan).Should(Receive(&receivedEvent))
Expect(receivedEvent.Name).To(Equal("Running"))
Expect(receivedEvent.Value).To(Equal("dummy data"))
})
In this way, we can read the desired property directly and put fine-grained conditions in the unit test.
For example, we can put different conditions in the following way.
It("Receive and get struct data", func() {
eventChan := make(chan Event, 10)
go func(ch chan Event) {
ch <- Event{
Name: "Running",
Value: "dummy data",
}
}(eventChan)
var receivedEvent Event
Eventually(eventChan).Should(Receive(&receivedEvent))
Expect(receivedEvent.Name).To(Equal("Running"))
Expect(receivedEvent.Value).To(Equal("dummy data"))
Expect(receivedEvent.Value).To(And(
ContainSubstring("dummy"),
ContainSubstring("data"),
))
Expect(receivedEvent.Value).To(Or(
ContainSubstring("dummy"),
ContainSubstring("-------"),
))
Expect(receivedEvent.Value).To(Or(
ContainSubstring("-----"),
ContainSubstring("data"),
))
})
Receive channel data multiple times
We might need to write a test case where the channel sends data multiple times. Just repeat the statement as many as you want.
It("Receive data multiple time", func() {
eventChan := make(chan Event, 10)
go func(ch chan Event) {
ch <- Event{
Name: "Running",
Value: "dummy data",
}
ch <- Event{
Name: "Stop",
Value: "Let's take a break",
}
}(eventChan)
var receivedEvent Event
Eventually(eventChan).Should(Receive(&receivedEvent))
Expect(receivedEvent.Name).To(Equal("Running"))
Expect(receivedEvent.Value).To(Equal("dummy data"))
Eventually(eventChan).Should(Receive(&receivedEvent))
Expect(receivedEvent.Name).To(Equal("Stop"))
Expect(receivedEvent.Value).To(ContainSubstring("take a break"))
})
In this way, we can control the order of the data.
Comments