Today we will be continuing our conversation from the previous article: Query Objects. We will be using RSpec to unit test this new type of class. If you are not familiar with RSpec, they have some really phenominal docs.
Well let’s dive right in! First, Let’s capture our code from the previous episode (with some minor corrections).
Now let’s wave our magic test wand and see what happens…WOOSH!
Wow, well that was rather unexpected, but we do have our tests here for us. Now I’m not gonna lie to you. There is a lot of little details like factory bot usage, scope definition, and time-sensitive-testing that I’m just going to ignore so we can talk about the important pieces. Without further ado, let’s jump in.
First thing you might notice is that this is a rather long test file. The truth is that this query object covers six different combinations of queries. This means we need to create Purchase objects to fullfill the conditions of each of our scenarios. We also need to test different instances of the query object that contain our six different filters. This is why you see “let” all over the place. This allows us to setup our assertions.
The second thing you might notice is that there is zero mocking or stubbing here. You might say something like “but these tests are coupled to the DB so these tests will be slow”. Again, you would be right. There are a couple problems with mocking here. First, we would need to create a mock that behaves like an activerecord scope. Second, we would need to to assert the correct methods are called with the right SQL/arguments. This is pretty complex to setup and in my opinion, takes away from the readability of the tests. I would also argue that a good unit test here would be to run “query.resolve” with various inputs and only test the output. You can read more about unit testing from men smarter than me like martin fowler. At the end of the day we want to make sure that given a collection of Purchase objects…this query returns the correct results given a set of filters.
If any of the techniques or code in this example is unclear please drop a comment and I will respond. The project this code lives in is available on Github.
As always, thank you for taking the time to read my ramblings. You truly are the best!