4 ms·
I find the mocking/stubbing in Go awkward. You have to implement all mocked methods, and if you want to record certain calls, you have a lot more code to write.
by cryptos 12y ago
I find the mocking/stubbing in Go awkward. You have to implement all mocked methods, and if you want to record certain calls, you have a lot more code to write.
Let's translate the example to Java:
public interface Job {
int getPriority();
void doWork(); // do is a reserved word
}
Then, to mock it with JMockit:
new Expectations() {{
job.getPriority(); result = 1;
job.doWork(); times = 1;
}};
These 4 lines replace all this Go code:
var Done bool
type DummyJob struct {
}
func (j DummyJob) Priority() int {
return 1
}
func (j DummyJob) Do() error {
Done = true
return nil
}
If you imagine some larger interface, it would be a lot of code in Go. Mocking in Go isn't fun.
- Jabbles 12y agotype DummyJob struct { Job } DummyJob now implements Job, you can override whatever methods you need. http://golang.org/ref/spec#Method_sets http://golang.org/ref/spec#Method_sets
- rancor 12y agoThanks for the tip, for some reason it never occurred to me to do this for large interfaces.
- cryptos 12y agoThis looks much better, although not as simple as mocking in Java with an advanced mocking tool like JMockit. It could be hard to avoid accidental integration tests with this approach.
- rancor 12y agoThe other half of the answer to this problem is to favor small interfaces, ideally a single method. For example, a UserStore interface could be quite large, but if it's composed of UserCreator, UserUpdater, UserFinder, and UserDestroyer subinterfaces, then you can easily arrange to only use the small, task specific, interface. Agreed that it's all quite a bit more of a pain than using a serious mocking toolkit, though changes in testing practices and API design help substantially.
- Jabbles 12y agoDo you mean that you're worried that calling a method on DummyJob will accidentally run some code you don't expect? If you don't initialize the embedded interface, non-overridden methods called on the struct will panic. http://play.golang.org/p/4aqGof1ikp http://play.golang.org/p/4aqGof1ikp
- MetaCosm 12y agoComparing vanilla to tooled seems a bit strange. Why not compare JMockit to gomock or something?
- jacques_chester 12y agoFunnily enough, a lot of Java code does what the Go code did: it mocks by defining interfaces and then attaching either production or mock code as required. See SpringMVC for an example. Why? Because Java mocking libraries aren't perfect either. If you're using an injection framework, it tends to be trickier to arrange the injection of the right object in the right place and when you don't, the breakages tend to be mysterious. Then you get into the exciting world of bytecode manipulation, which is necessary for some mocking libraries. And, most annoyingly, it's basically impossible to mock static methods. I've worked in projects that used extensive Java mocking and on a project that used Go with hand-rolled mocks. I experienced far less pain with Go, because everything was plain and simple. All we needed was to verify behaviour and we did that with relative ease. I'd note that the only 3rd-party libraries we used were Ginkgo/Gomega, to support a more spec-style of testing.
- lmm 12y ago> Funnily enough, a lot of Java code does what the Go code did: it mocks by defining interfaces and then attaching either production or mock code as required. See SpringMVC for an example. I actually support this; I just think it's worth being explicit about the relationship between interface and implementation. The difference between Camera.shoot() and Gun.shoot() can be important, and when you want to upgrade a library you need to know what you're implementing. In Java you have extends and @Override to give you a straightforward compile error when a parent interface changes, which makes library upgrades relatively painless. I fear the Go approach gives you the worst of both worlds - the extra typing of a Java-style explicit interface declaration, but no more safety than a Rails upgrade. > Why? Because Java mocking libraries aren't perfect either. If you're using an injection framework, it tends to be trickier to arrange the injection of the right object in the right place and when you don't, the breakages tend to be mysterious. I don't think you're comparing like with like. If you're using an injection framework in Go, you'll have the same difficulty injecting and mysterious failures when you get it wrong. There's no need to throw the Java baby out with the Spring bathwater. > Then you get into the exciting world of bytecode manipulation, which is necessary for some mocking libraries. > And, most annoyingly, it's basically impossible to mock static methods. On the contrary, the whole point of bytecode manipulation is that it lets you mock static methods (or undeclared interfaces) - you don't need it otherwise. Is mocking statics any easier in Go? If Go simply doesn't let you have statics, that's not really a Go advantage - you can (and I would argue should) program without statics in Java 99% of the time, but they're there for when you really need them.
- TeeWEE 12y agoDont forgot that the java mocking you are proposing requires JMockit, which in turn requires Reflection, and requires you to understand how JMockIt actually works... There is a lot of bookkeeping. In the go example, its just pure core go language, nothing more is needed todo the mocking. In my opinion the go example is a lot simpler than the java example that requires a big mocking framework. Not to forget, that in go, you can implement interfaces around a simple type such as a int, yes a simple int can implement a whole interface. And there is no explicit bookkeeping needed
- MetaCosm 12y agoThere are also partial mocking libraries for Go. http://blog.getsocialize.com/2013/getting-a-handle-on-testing-and-mocking-in-golang http://blog.getsocialize.com/2013/getting-a-handle-on-testin...
- tonyhb 12y agoI just added a comment to the CloudFlare blog about 'semi mocking'. In go, functions are first class. You can assign them to variables. Using this, you can 'hot swap' methods in your tests to check for each different scenario. Here's an example: https://gist.github.com/tonyhb/8153e1fe10891c68a8c5 https://gist.github.com/tonyhb/8153e1fe10891c68a8c5 By changing the value of 'n' in each test you can check any failure scenario you need to. It's really, really awesome.