1

I have a few specifications that test code performing culture-aware conversions. I want to set a defined culture for my tests so that I can hard-code the expected values without having to worry about the configured culture of the system running the tests.

Is there a simple way to do this with Machine.Specifications or do I have to set Thread.CurrentThread.CurrentCulture (and possibly CurrentUICulture as well)?

4

1 回答 1

6

MSpec doesn't have any built-in facilities for changing the Thread's culture. However, it has several general methods for doing "something" before and after a test.

The "easy" way is to just use the Establish and Cleanup delegates.

[Subject("Culture Sensitive Tests")]
public class When_doing_culture_sensitive_stuff
{
    Establish context = () =>
    {
        OldCulture = Thread.CurrentThread.CurrentCulture;
        Thread.CurrentThread.CurrentCulture = NewCulture;
        Thread.CurrentThread.CurrentUICulture = NewCulture;
    }

    Cleanup cleanup = () => 
    {
        Thread.CurrentThread.CurrentCulture = OldCulture;
        Thread.CurrentThread.CurrentUICulture = OldCulture;
    }

    Because of = () => Subject.DoSomethingCultureSensitive();

    It should_do_something_culture_sensitive = () => ...;

    private static CultureInfo OldCulture;
    private static CultureInfo NewCulture;
}

But, you'd need to share that in every test that requires it. So, I recommend a helper class that does the switching.

public class ChangeCurrentCulture : IDisposable
{
    private readonly CultureInfo original;

    public ChangeCurrentCulture(CultureInfo culture)
    {
        original = Thread.CurrentThread.CurrentCulture;
        Change(culture)
    }

    private void Change(CultureInfo culture)
    {
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = culture;
    }

    public void Dispose()
    {
        Change(original);
    }
}

And you can setup the call in a shared base class. The delegates get chained when you run the specs.

public abstract class CultureSensitive
{
    Establish context = () =>
    {
        Change = new ChangeCurrentCulture(NewCulture);
    }

    Cleanup cleanup = () => 
    {
        Change.Dispose();
    }

    private static ChangeCurrentCulture Change;
    private static CultureInfo NewCulture;
}


[Subject("Culture Sensitive Tests")]
public class When_doing_culture_sensitive_stuff : CultureSensitive
{   
    Because of = () => Subject.DoSomethingCultureSensitive();

    It should_do_something_culture_sensitive = () => ...;
}

Another option depends on you separating all of the culture sensitive tests into a separate assembly. The IAssemblyContext interface gives you two assembly-wide setup and cleanup methods. You could change the culture for all the specs in that assembly (don't worry about cleanup).

public class CultureSensitiveTests : IAssemblyContext
{
    public void OnAssemblyStart()
    {
        Thread.CurrentThread.CurrentCulture = NewCulture;
        Thread.CurrentThread.CurrentUICulture = NewCulture;
    }

    public void OnAssemblyComplete()
    {
    }
}
于 2013-06-07T16:21:24.220 回答