I have an application that returns data dependent on the time specified, I can specify days, months or years. The issue is that if I were to run the application today and ask it to return data from 1 month ago and in 3 months time I were to ask the application to return data from that date for the previous 1 month (i.e. 1 month from date) the results will obviously be different. Due to the dynamic nature of this I am finding it difficult to create unit tests because I have to change the date depending on when I am running the tests. Does this symbolize bad design or is this an exception case?
It is a problem case - but not necessarily bad design.
For the unit testing, you need to provide fake modules to handle the generation of time and time-based data. This is similar to what you need to do with handling databases.
(And I'm still not sure how people like me, who provide DBMS, are supposed to do unit testing when the unit testing people all assume "thou shalt fake out the database", but that's a separate discussion.)
Maybe. This is surely a symptom of a coupled design. BTW this is a very good and complicated question.
I hope to read a better answer than this one.
One way round your specific problem would be to encapsulate the time itself in a separate object and then in your testing you could force this object to return some known time.
You could rewrite it so the piece that get's the current date and the piece that parses the date are separate. In other words you get the date in one function and pass it's result to another function that parses it. That way you can test the parsing by passing in a fixed date.
It's not necessarily a bad design, but the software hasn't been designed for easy testing, and design for easy testing is considered by many people to be a necessary aspect of a good design.
If the code could be modified to find data from 1 month of a specified date, the production code could easily pass the current date, and the test code could use a fixed date.
This is a common problem, especially when it comes to creating reports. I see three easy ways of going about this:
1) Roll back/forward the system's clock.
2) Update the date on the test data to always be within your range (ie update db set date = now()).
3) Run a query on a clean system, then apply your changes, and run the query again. This is probably the easiest way.
After re-reading your question, I'm not sure what you're trying to test.
- Are you trying to test that the database returns the correct data for the query you are running?
- Are you trying to test that your code generates the appropriate query?
- Are you trying to test that your code processes the returned data appropriately?
If #1, then maybe you could use an in-memory database that you could initialize in your unit-test with fixed data. I've used H2 before for that purpose. Search for embedded databases.
If #2 or #3, you should be able fixture the data for the test and get back known results. If the sql generation or result processing is embedded then you can use mocking to mock-out the receiving methods.
For example, I usually have a class called QueryRunner that accepts the SQL that I want to run and a class that it will call to process the result set. That way I can mock QueryRunner and see if it is called with the SQL I was expecting and if it was called an appropriate number of times. Much easier than trying to actually mock the JDBC classes.
If the test data changes, it means it's kind of "live" database. Why would you run your test on such a database ? If the tests results are not the same between two runs, it goes against the goal of a test, an unit test should be repeatable and reliable, if you're rewriting it every couple of months it's neither ...
You probably need a "static" database for your tests, once you're added all the data for the different scenarios/uses cases you need to test, you could do a backup and you'll restore it whenever the data gets "corrupted". Or even better, you have scripts that will insert all those data into an empty database before running the test suite.
I think, if it is truly unit testing, it is a bad design. Your unit/class has a dependency on time, if there is no way to mock out the time then the "unit" isn't a unit. To unit test you need to be able to influence all of the external dependencies that affect the flow of control. In practice this means that there is a class that is responsible for selecting the information you need that accepts some delegate that's responsible for the time. This might be a Calendar object or something similar.
If it is a complete module that you are testing, perhaps you are not unit testing. The same principle carries though, if time influences the output radically then date should be an optional input not only for testing, but because more often than not systems need to be run for a specific date. For some this might be speculative: but it is better practice not to refer to global state directly in modules deep in the system but to pass this state around. The difficulty in testing can turn into a difficulty in maintenance pretty easily. All that would need to happen would be for a customer to suggest a command to, say process batches (or whatever you're doing, for a particular day again because the system crashed. Burying the date deep, and possible multiple-times in the system when it is critical for the selecting logic can lead to greater maintenance later.
If your system has a great number of dependencies (is complex) you might want to consider a Container to manage Dependency Injection. These containers, often called IoC Containers allow the system to be written as loosely coupled objects that and these are "wired" together by the container through configuration of some kind.
What is to prevent your test from supplying the data for your code to test against?
This can be achieved in two ways, either use some other method to programatically place the data into your data source or mock your data source.
Personally I would be tempted to change my code so that the datasource was accessed via an interface derived class. That way you can mock your datasource and supply known data which isn't dependent on time.
If you are using Visual Studio then you can use it to automatically create an interface class for your datasource object and modify it to use that interface as a base class.
What you are doing is not a unit test. Unit tests should exercise a small "unit" of your code and your code only. By incorporating the system time, you are also testing the environment in which you are currently running. That is a system test. Unit tests are very effective tools to make sure that the code you wrote was written correctly, but it helps a lot of you write your code in "testable" manner.
There are a few tricks that are easy to learn but difficult to master that will help you write testable code. They generally all follow the same pattern of creating what they call "seams" in your code and then injecting "stubs" or "mock objects" into those seams at test time.
The first important thing to figure out is where your seams go. This isn't that hard. Basically, any time you construct a new object, that's a good place for a seam. A prerequisite for this rule is that you have a pretty decent object-oriented design to begin with. (The guys over at the Google Testing Blog argue that you cannot unit test imperative code because you can't do dependency injection.) The other good place for a seam is any time you talk to an external data source, like the operating system, the file system, a database, the Internet, etc. This is what you are doing.
You need the system time. That's where your seam should go. I recommend you get a good book on this for a full treatment of all your options here, but here's one example of what you could do. There are at least 2 or 3 other ways to "inject your dependency" on the current system time. I'll use Python for pseudo-code, but it works in any OO-language:
class MyClass(object):
def _get_current_time(self):
'''This is a test seam'''
return datetime.datetime.now()
def age(self):
return self._get_current_time() - self._birthday
Then in your test code, do this:
class FakeMyClass(MyClass):
def __init__(self, test_time, *args, **kwargs):
self._test_time = test_time
MyClass.__init__(self, *args, **kwargs)
def _get_current_time(self)
return self._test_time
Now, if you test with FakeMyClass
, you can inject whatever system time you want:
myclass = FakeMyClass(t)
self.assertEqual(myclass.age(), expected_age)
Again, this is a pretty big topic, so I recommend getting a good book.