Handling unit tests with a condition on the curren

2020-02-26 06:16发布

I'm taking a stab at setting up unit tests for some utility classes in a project I'm working on, and one of the classes (contains licensing info) has a method that does some determination based on the current time.

i.e. the license contains an expiry date, and the license string validates that date, but the actual logic to see if the license is expired is based on the current time.

public boolean isValid()
{
    return isLicenseStringValid() && !isExpired();
}

public boolean isExpired()
{
    Date expiry = getExpiryDate();
    if( expiry == null ) {
        return false;
    }

    Date now = new Date();

    return now.after( expiry );
}

So, I'm not sure what to do, since the 'new Date()' thing isn't a static criterion.

  1. Should I not bother to test 'isValid', and just test 'isLicenseStringValid()' and the 'getExpiryDate()' function separately?
  2. Do I just use a license key in the test with a crazy long expiry such that I'll have switched jobs by the time it expires?
  3. Do I try to mock out 'new Date()' to some 'getCurrentTime()' method such that I can fake what time it is now?

What do others normally do with tests that are time-conditional?

6条回答
\"骚年 ilove
2楼-- · 2020-02-26 06:41

Use dependency injection and inject a TimeProvider that provides a getExpiryDate() method.

查看更多
男人必须洒脱
3楼-- · 2020-02-26 06:45

Definitely mock out new Date().

Create a Clock interface with a getCurrentTime() method or something similar. That way you can have a FakeClock for testing and a SystemClock which uses System.currentTimeMillis() or whatever.

I've done this a number of times - it's worked extremely well. It's logical too - effectively you require a "current time service" so that should be injected like any other dependency.

查看更多
家丑人穷心不美
4楼-- · 2020-02-26 06:46

All three approaches are possible:

  1. don't test: lazy man's way
  2. use a license that won't expire for ages until you've left the job: cover my ass way
  3. use a mock for the current date, such as a TimeProvider: the perfectionist way

I'd go for a comprimise: I'd add the current date as a parameter to the isExpired method, and the isValid method. For your live production code, add a simple isValid() no-arg override that calls isValid(new Date()). Your test code uses the version that takes the current date as the parameter.

查看更多
看我几分像从前
5楼-- · 2020-02-26 06:49

I usually inject a date provider into the tested code. This also helps if you need to switch conventions or otherwise "fix" the time testing code.

查看更多
等我变得足够好
6楼-- · 2020-02-26 06:51

If you feel the TimeProvider/Clock abstraction is too overboard perfectionist (which may very well be the case), consider this instead

Make getCurrentType protected virtual, then create a TestingProductionType decendant of the ProductionType that contains the code you posted. In that type, override the getCurrentType() method to return some deterministic result. In your unit test, create an instance of this TestingProductionType instead.

Viola, the dependency of current time is now removed from your unit tests. The only production code that is now not unit tested is a method with a single line returning new Date(). I could live with that.

查看更多
甜甜的少女心
7楼-- · 2020-02-26 06:55

If you can check out Mole at http://research.microsoft.com/en-us/projects/pex/ Moles allows to replace any .NET method with a delegate Just use it to replace Date and have it return what ever you need. Then you don't need to do anything crazy.

-Raul

查看更多
登录 后发表回答