So today, we'll look at how we can provide different results based on different parameters and how we can count the number of times a method is called.
As a side note, I have been doing some experimentation with Foq (I use Moq pretty extensively in the C# world). Unfortunately, I've been failing at the implementation; my F# skills need some work. Once I get that figured out, I'll show how that is better option when dealing with more complex scenarios. [Update 12/30/2016: It looks like my issues are related to the test runner, not my F# skills. More to come.]
[Update 1/4/2017: It turned out to be a runtime version issue. Read more here: Trying to Get Foq Working with NUnit Test Runners.]
Simple Method Mocking
In the last article, we looked at mocking using a simple object expression:
Simple Method Mocking with an Object Expression |
This scenario is pretty simple. We have an interface (ISolarService) which has a single method (GetServiceData). The object expression (inside the curly braces) allows us to specify that if anyone calls the "GetServiceData" method, it should return the results in our "goodData" object (this is just a string with JSON data that's specified at the top of the test class).
Date/DateTime Functions
I made another change to the code since we looked at it last time. Notice that the test function above has calls to "date" and "dateTime". I created these functions to adjust the syntax a bit. Previously, I was creating DateTime objects directly, and the code looked like this:
Code with Original DateTime objects |
I played around with syntax a bit and came up with these two functions:
These functions wrap a call to the DateTime constructor. I'm not sure if I'm 100% happy with this, but I do like how things look in the calling code when we call "date" and "dateTime". It seems to fit in better with the F# style.
Along with this, I added the intermediate items "requestDate" and "expected". By adding these, I could eliminate the parentheses later on. Again, I'm not sure if this is a good way to go, but I do like the syntax a bit better here.
Different Parameters, Different Results
While I was exploring mocking, I ran across a sample that showed a really easy way to have different result sets returned from a method based on the parameter. (Sorry, I didn't keep track of the article, so I can't point back to it.)
To have different result sets come back, we can expand our object expression to use pattern matching in the body of the member:
Simple Method Mocking with Pattern Matching |
In this code, we don't ignore the parameter. Instead, we grab onto it so we can match against it.
So if we pass in the "requestDate" (which is Nov 24th 2016), we return our "goodData" string. But if we pass in any other value, we return our "badData" string.
This is strictly not necessary for this particular test, but if we had a shared mock object, it could be useful in providing different results for different tests or parameters. (It would also be useful for property-based testing, but that's a topic for another time.)
So we can expand our object expression to be a bit more flexible. And it doesn't take much code to do this (and that makes me happy).
Counting Method Calls
Another area where I've used mocking is to count the number of times that a method is called. This is primarily to check to see if a client-side cache is working, i.e. multiple calls to the client method should only result in a single service call.
With Moq (and Foq), we can do this by using the "Verify" method. And I've done this quite a bit with Moq in my C# code. For a simple scenario (like we have with the Sunrise/Sunset library), we can do the counting manually with just a little bit of code.
Here's an example:
Simple Method Call Counting |
Each time the "GetServiceData" method is called, we increment the "callCount" variable. To test this, we call "GetSunset" twice with the same parameter. We don't care about the results of those method calls, so we just ignore them.
But then we check the "callCount" to make sure that the service method inside our mock object was only called one time.
Here's a test that shows something similar:
Simple Method Call Counting |
In this case, we call "GetSunset" with different parameters, so we expect that the service is called twice.
There are a couple things I'd like to change about this code. I would like to have the counter (callCount) be part of the object expression. In the current state, it seems to "leak" since the counter is completely separate from the object.
Another thing about this code is that it only works with a single method. If the mock object had multiple members that we wanted to track, then things would get quite a bit more complicated.
I'm sure that I could write that code, but what I'd really be doing is writing my own mocking framework. And that's something I definitely don't want to do. I'd rather leave that to the experts who have already done this.
Mocking Frameworks
These examples work great with very simple scenarios. In this case, we only have a single method in the interface. But as soon as we have multiple methods, it's pretty common to only want to provide mock implementations for only one or two of them. In addition, we may want to track calls to multiple methods. That's where our code breaks down.
I've taken a quick look at Foq (and even Moq) in my F# tests, and that's where I found that my F# skills are still lacking. I seem to be doing okay at compile time (everything builds), but I'm getting errors at run time. So I need to figure out where I'm going wrong. I'm pretty sure it's around the code quotations. Once I get that worked out, I'll do some more exploration and write up my results.
[Update: 12/30/2016: It looks like the problems I've run into with Foq are related to the test runner that I'm using and not because of my F# skills. I can get test code to run in the REPL, but I get exceptions with using the NUnit test adapter. I've got a bit more to figure out here.]
[Update 1/4/2017: It turned out to be a runtime version issue. Read more here: Trying to Get Foq Working with NUnit Test Runners.]
The more I work with it, the more I like the idea of using F# for testing my C# code. Unit tests are generally functional in nature: discrete inputs/outputs, no side-effects. So it seems like a good fit. Plus it gives me a chance to get better at F# using small steps.
One thing about being a programmer: it's impossible to get bored.
Happy Coding!
No comments:
Post a Comment