I wanted to create a unit test using MS Fakes Framework to replace TimeZoneInfo with Shim (example how to do it see e.g. “Using shims to isolate your application from other assemblies for unit testing” )
Initially I found that ShimTimeZoneInfo wasn’t generated. After some search I found that Microsoft made some decisions for us/:
What we’ve seen is that user simply want to shim a few type(s) in it. Due to this, we selectively shimming the commonly used type in the system/mscorlib.
But they didn’t mention it in the documentation.
The actual classes that are generated can be see in FakesAssemblies .fakesconfig files\ (e.g. mscorlib.4.0.0.0.Fakes.fakesconfig)
Fortunately, you can request the compiler to generate shims for specific objects(http://stackoverflow.com/questions/16154182/shims-are-not-generated-for-net-methods)
There are more details in “Code generation, compilation, and naming conventions in Microsoft Fakes”. However note that examples have missing version in lines like
<Assembly Name=”System” Version=”4.0.0.0″/>
So do not copy them literally.
You can specify only what you need and disable StubGeneration
mscorlib.fakes:
<Fakes xmlns=”http://schemas.microsoft.com/fakes/2011/“>
<Assembly Name=”mscorlib” Version=”4.0.0.0″ />
<StubGeneration Disable=”true”/>
<ShimGeneration>
<Add FullName=”System.Environment”/>
<Add FullName=”System.TimeZoneInfo”/>
<Add FullName=”System.DateTime”/>
</ShimGeneration>
</Fakes>
My code also used DateTime.ToUniversalTime, but even ShimDateTime class was generated, particular ShimDateTime.ToUniversalTime was not generated. I haven’t found a way to generate it.
After decompiling I found that the method is a wrapper of TimeZoneInfo method
[__DynamicallyInvokable]
public DateTime ToUniversalTime()
{
return TimeZoneInfo.ConvertTimeToUtc(this , TimeZoneInfoOptions.NoThrowOnInvalidTime);
}
but the overload DateTime ConvertTimeToUtc( DateTime dateTime, TimeZoneInfoOptions flags) is internal (Shim has methods for public overloads static public DateTime ConvertTimeToUtc(DateTime dateTime) and static public DateTime ConvertTimeToUtc( DateTime dateTime, TimeZoneInfo sourceTimeZone))
I’ve had to change in my code
DateTime utcTime = inputDateTime.ToUniversalTime();
to
DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(inputDateTime);
to make it testable.
Because Fakes are available in VS Premium/Ultimate editions 2012/2013 and Enterprise 2015(i.e not available in Community and Professional Editions) I wrapped the test methods in #if !FAKES_NOT_SUPPORTED conditions, to allow easily exclude them on Community and Professional Editions
#if !FAKES_NOT_SUPPORTED
[ TestMethod()]
public void ConvertServerTimeToTimeZoneTimeTest()
{
DateTime inputDateTime = DateTime.Now;
string zoneId = “China Standard Time”;
DateTime timeZoneTime;
//Act
using ( ShimsContext.Create()) //Uses Fakes assembly because TimeZoneInfo may change depending on daylight saving,
{ // hook delegate to the shim method to redirect
TimeZoneInfo.ConvertTimeFromUtc(utcTime, timeInfo);
ShimTimeZoneInfo.ConvertTimeToUtcDateTime = (inputTime) => inputTime.AddHours(-10);
ShimTimeZoneInfo.ConvertTimeFromUtcDateTimeTimeZoneInfo = (utcTime, timeInfo) => utcTime.AddHours(+8);
//Act
timeZoneTime = DateTimeHelper.ConvertServerTimeToTimeZoneTime(inputDateTime, zoneId, true);
}
//Assert
var diff = timeZoneTime – inputDateTime;
diff.Hours.Should().Be(-2);
}
}
#endif //!FAKES_NOT_SUPPORTED
To run Fakes, it’s required to replace legacy MStest.exe test runner with new VSTest.Console.exe- see my post Replace MSTest to VSTest to support Fakes.
It also was required to do changes in build script, upgrade to latest versions OpenCover and NSubstitute.
#fakes-shims-test