@dfc asked me on twitter what kind of blog posts on testing I’d like to see on twitter. Personally, everything. How to get started, what the value is, and such. I’d love to hear from the rest of you if there’s additional topics on this. I’m new to testing, but have been diving into it for the past two weeks or so.
As I mentioned on twitter “everything” isn’t very actionable. If you can come up with a list, preferably in the user story form of “As an ___ how do I ___ ?”, then I can find or write that documentation.
I’d be really interested to see some tutorials or blog posts on how to test or test around methods that have no return values, testing struct components, and testing interfaces.
These are problems I’ve been having myself lately and the answers I’m finding are usually that I should be writing code such that I always produce return values. This makes sense in the first case, but doesn’t really cover the latter two. And either way it would be nice to get some guidance on best practices regarding extraneous return values.
Thanks so much for opening this topic!
EDIT: I should clarify that I mean explicit return value. My specific case is unit-testing writes to a database. I could add “RETURNING id” to every query I have and then check that, but I am wondering if there is a more elegant way.
@dfc I know “mocking” bit me recently. mgo is such a hard package to mock and the mocking solutions out there required someone to litter up their code with annotations and such that I didn’t care for.
I ended up abstracting out all the database calls to its own unexported functions and then mocking a database object that was passed into the function. Things like that, I don’t see solutions or anything like it online. It could have been really useful.
I know I talked to other developers that use MongoDB and I asked them how they TDD/Unit test their database work and they were like, “We stand up a server and test against it.” There has to be a better way.
Wow! I was logging the errors, rather than returning them. Thanks so much for that insight! That solves that problem. I guess now I’m just curious about strategies around testing structs/interfaces and if there is anything in addition to the error that I should be checking on those functions-that-call-functions.
One thing that might be good, for people who have experience with other languages, would be some simple blog posts comparing how you write tests in language X to how you’d do the same thing in go?
You could pick a small number of popular languages that have different testing paradigms - Java, Ruby, Python, etc. And do some apples to oranges comparisons with simple test cases, with a special eye on the pain points you might face - for example, ruby testing is really “easy” because ruby is ruby and mocking and stubbing are easy. But with Go (and languages like Java or .NET) you need to design your code with testability in mind, using interfaces and paying closer attention to your dependencies.
That might start to drift away from just being a pure testing series of blog posts, but I’ve often found that to write proper tests, you need to write code that is testable to begin with, so the two sort of go hand in hand. It also might help people to better wrap their head around the whats/whys of writing tests in go, if they have a familiar analog to compare it to.
As a novice go developer, how do I know what to test?
As a novice go developer, how do I test against a database?
Depends, do you want to test that your database driver is working ? (someone else has probably tested that). Do
As a novice go developer, how do I mock?
Depends what you want to mock. If you are testing that your function calls a method on a value passed to it, maybe use a mock, but as yourself, are you just testing that method dispatch in your language works ?
As a novice go developer, how do I get started in testing?
I know this is a mostly facetious reply, but if you write code, then you write tests, you’ve done it wrong. You must write the test first, then write code to fix the failing test.
Addendum: Obviously, sometimes you cannot write the tests first, because you don’t know what the code looks like, you’re just exploring the design. That’s fine, that’s what spike are for. The key is, when you’re done with your experiment throw that code away and rewrite it using TDD.
I agree with you, but I still feel sometimes when I am doing something I’m not entirely sure how to do, writing a test first is extremely hard. I can imagine for a novice developer it will be difficult as well. I think maybe a concrete example of a small application that showcases the common things to test could help show how testing should be done. A lot of that is self-explanatory once you have learned it, i.e you should test if your function returns what you expect and handles all possible types of input, not just the expected “good” values.
“Spike” is a term for a quick cut at a solution to a problem that you don’t intend to be production ready / “final”. Many times, they end up as final anyways, because of time constraints, etc etc.
While I agree that this is the way we all should write code, I think if we’re honest, in reality it’s not how code gets written in a lot of companies. Encouraging people to do the “right” thing is important, but so is just helping them to get going and learn how to write tests - even if they’re doing it in the wrong order. TDD is great - but not everyone adheres to it.
Personally, I find TDD great for building a domain model i.e. business rules, where dependencies point in rather than out because a) you don’t have to worry about mocking (which I’m not a fan of), and b) because there’s nothing more important to get right than the core reason for the application. On the outer edges of an application I tend to lean more towards plain old unit testing and integration tests. It works for me but as “they” say, YMMV.
Don’t; it destroys code architecture by splitting it apart at arbitrary points and destroys cohesion.
It might work, if you only have a single function to design, such as “sqrt”. Or when you have a good design vocabulary. But as a “noob” you won’t be able to see 5 steps ahead what problems your design decisions will create. This ends-up in pieces that fit together poorly, without any guarantee that they actually deliver any value to the end user.
Refactoring cannot get you out of it either – only rewriting can, which means you need to rewrite all your tests – essentially you end up producing a lot of waste.
Instead write checks for delivering value to the end-user. E.g. implement a user story – it will also ensure that all your pieces work together nicely, and gives pretty good coverage and ensures that you actually deliver something with your code. There’s less waste if you get it wrong and design mistakes become much more obvious.
Obviously spiking will help you reduce waste further. You won’t have to actually implement all the pieces to see how they interact. Also, during spiking don’t just spike the code, but also spike testing points (i.e. introducing interfaces / types / funcs).
Just trying to write tests – won’t work, because you cannot see the whole. Just writing the code – won’t work, because you cannot see the value needing to be delivered. Both need to be written together to create the least amount of waste in the process. By focusing on value you ensure that you are writing the things that need to be written.
The main reason not to mock a database in testing is that database vendors make changes that end up affecting the results you get.
For the past year or so I have been working on a Go, mongodb app that uses their map reduce feature that run just fine on mongodb 2.6 but when we tried to run the app on 2.4 it gave very different results, because we had tests running against the db, we were able to quickly detect them and work around them.
This also helped us when we moved to mongo 3.0, we knew our queries were going to work just as we expected them.