Testing and mocking - what clicked for me

I’m probably not alone in knowing that testing software is (should be) an incredibly important part of your development, but the exact minutiae of how to actually go about it is still somewhat shrouded in a fog.

Except my apps make network calls, manipulate json, import libraries, and so on, and for a very very long time I couldn’t move on from these simple (“does 5 + 5 equal 10?”) scenarios to actually testing what my app is (should be) doing.

Sometimes it’s not you. Sometimes the code really is untestable, and you have to organize code in a way it becomes testable.

In my specific case, I’m building a Go app that uses the haveibeenpwnd.com API to check whether the password the user gave me on registration has been exposed or not. I’m using https://github.com/masonj88/pwchecker/ for an easy way to check that, so I don’t have to write the API call again.

That module can return 3 different responses:

API call succeeded, password is not compromised

API call succeeded, password is compromised

API call did not succeed due to a network error (rate limiting, DNS went away, whatever)

What I wanted to do is test all these three eventualities, but came up to two very hard questions to answer:

how do I select a password that’s not compromised?

how do I even simulate a network failure?

Mocking

Until now I had very hazy ideas about what mocking actually is. Did not understand the need, did not understand where it fit into testing. Working through this specific problem clarified it, so here’s my best attempt at explaining to future me, and hopefully you’ll find it useful as well:

The starting point is that my app directly uses the pwchecker module, which means it’s coupled. The two cannot be separated, and whatever tests I would need to run would always use the actual module which would make actual network requests. Because of this, I can not test this part of my app without hitting the API.

Testing my app should happen without hitting the actual API. Moreover, I don’t even need to test the module itself, because that’s not my responsibility. The module has its own tests. What I need to test is what my app does in response to what the module returns based on the API calls.

Which means I don’t even need the module itself, I just need a way to give the same possible responses as the module. In this case:

a response that says password is not compromised, network call successful

a response that says password is compromised, network call successful

a response where the network call did not succeed

Which also means I can just create a mock copy of the pwchecker module that returns one of the three responses based on some specific input I give it. Then the idea is that when we’re in testing mode, my app would be using this mock copy of the module instead of the actual module, and then I could reliably trigger all the possible responses the live module could return.

However in order to do that, I need to be able to tell my app which copy of the module to use. Which brings us back to the first point: if the code is not set up correctly, I can’t tell my app which module to use, and therefore I can’t test it.

Interfaces

I need to make the code modular (pun intended, I’m no coward!), so that I can tell it to use the live pwchecker on production, and our mocked copy during testing.

That means instead of using module directly, I need to move it behind something I control, that can be initialized.

PasswordChecker interface is a generic interface that both the live version of the pwchecker, and the mock version of the pwchecker will implement. It has one method called IsPasswordPwnd, so our app code can be sure that it can call that method

Handlers is a struct. It’s kind of like an object in PHP land. It has one named property called pwc, which is going to be of type PasswordChecker, the interface. Which means every Handler will have an implementation of that interface under the pwc property.

NewHandler is a function that takes a PasswordChecker interface (or implementation), and returns a new Handlers struct where the pwc property is already defined as the first argument to the NewHandler

lastly I’ve moved RegisterPost to belong to the Handlers struct, where the h variable refers to the struct itself (kind of like calling $this from within an object method in PHP). Because of this, we know that h.pwc.IsPasswordPwnd a function that takes a string, and returns a boolean and an error (or nil on either)

Okay, how to actually use this as we’re not done yet!

First we need to create both the live, and the mock implementations of the PasswordChecker interface, and pass either of them to the NewHandler function.

Because the testing code is still under the same package main, we don’t need to include a lot of the code around the interface and functions and methods.

This particular test will simulate what would happen if the library returned with a network error.

Possible problems

Because while testing we aren’t actually using the live module we need to make sure that the mock copy stays in sync with the live module.

What happens if the module changes the return types or changes the number of different things it can return, and we don’t notice for a while?

A possible solution to this would be if package authors would not only create the package, and the tests, but also a mock object we could import just as easily as the actual module.

Conclusion

Interfaces and dependency injection are nice (even if that’s not exactly what’s happening here). Being able to configure how something behaves and not being locked in to one specific implementation is superbly powerful.

I know this was probably one of the more difficult to digest blog posts of mine. Let me know if something doesn’t make sense here, or if I can improve it in any way.