How can I set the culture for individual MSTest test method(s)?
Asked Answered
C

2

6

After recently finding an error that was due to not specifying InvariantCulture when parsing text into numbers, I wanted to beef up our unit tests so that they would catch the issue. I wrote a new test that changed the current culture to one with different numeric formatting, re-ran one of the existing test methods, then set the culture back. Unfortunately, the culture change also affected other test methods. Is there a way to do this such that the tests do not interact?
I did find that putting the culture-setting test last in the file would "solve" the problem, but I hate to rely on the test ordering since it's not guaranteed. In the following example, with a system culture of "en-EN", TestMethod1 and TestMethod2 will succeed if run by themselves. If I run all of the methods together, they all fail.

   [TestClass]
   public class UnitTest1
   {
      [TestMethod]
      public void TestMethodGerman()
      {
         var originalCulture = Thread.CurrentThread.CurrentCulture;
         Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE");
         TestMethod1();
         Thread.CurrentThread.CurrentCulture = originalCulture;
      }
      [TestMethod]
      public void TestMethod1()
      {
         double value = Double.Parse("3.00");
         Assert.AreEqual(value, 3.0);
      }

      [TestMethod]
      public void TestMethod2()
      {
         double value = Double.Parse("4.00");
         Assert.AreEqual(value, 4.0);
      }
   }
Cathartic answered 3/9, 2015 at 18:21 Comment(0)
C
0

The German call to TestMethod1 is failing, which causes the line that resets the culture to never get executed. Put that line in a finally block and the tests should execute as expected:

var originalCulture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE");
try
{
    TestMethod1();
}
finally
{
    Thread.CurrentThread.CurrentCulture = originalCulture;
}
Cordie answered 3/9, 2015 at 21:31 Comment(6)
You definitely need the try/finally mentioned in the answer to ensure the culture is reset, but I think you may still have a problem with tests running with an unexpected culture. MSTest runs tests in parallel and I am pretty sure it doesn't start a new thread for each test, but instead has some way to dispatch multiple tests on one thread. I guess you will have to try the try/finally and see if your problem goes away, but if it does not, I think what I mentioned will be the source of your problem. As far as I know, there is no way to force MSTest to run the tests sequentially.Crosscurrent
I believe MSTest only runs in parallel if you explicitly enable it, but the point is taken that this makes the error prone. It would probably be best to spin off another thread to do this work so it is isolated and controlled.Cordie
I think that link contains old information from VS2010. In VS2013 (and assumably VS2015) it runs tests in parallel by default. I'm not sure the config settings event still exist.Crosscurrent
As far as I know, the settings file may be different, but the limitation of parallel tests in MSTest is still present. There are comments from August of this year on this uservoice request: visualstudio.uservoice.com/forums/121579-visual-studio/…Cordie
The try/finally is what I needed, thanks. As for running tasks in parallel, I shouldn't need to worry about tests run on other threads because I'm only setting the culture for the current thread. Even if tests were parallel & weren't run with one thread per test (1 thread per core, typically), I would still expect each thread to be running just one test at any given time. Given the amount of work it would be to invent some sort of sub-thread scheduling to interleave multiple tests on a thread, and the number of tests likely to break if they did that, I'm not expecting Microsoft to do that.Cathartic
@Cathartic this will work even if tests are run in parallel. The wrapper and the inner test method run on the same thread.Daredevil
F
3
[TestMethod]
public void FooTest()
{
    var foo = new Foo();
    var actual = ExecuteWithCulture(() => foo.Bar(), "en-US");
}

private T ExecuteWithCulture<T>(Func<T> methodFunc, string cultureName)
{
    T result = default(T);

    var thread = new Thread(() =>
    {
        result = methodFunc();
    });

    thread.CurrentCulture = new CultureInfo(cultureName);
    thread.Start();
    thread.Join();

    return result;
}
Ferri answered 10/1, 2017 at 15:56 Comment(6)
Why use a Task for this? Why change a threadpool thread's culture without resetting it? This could result in "interesting" behaviourDaredevil
Good point! Do you know of a better/simpler threading approach?Ferri
Why use a different thread at all? This isn't a question about multithreadingDaredevil
If unit tests are run in parallel, John's answer will not suffice. Tests will fail intermittently. Using a thread is the only reliable way.Ferri
First, nobody asked about running tests in parallel. Second, John's answer simply wraps a method with a try/catch. It's the wrapper that will be called by the test runner. Even if tests run in parallel, that code will work because the test thread is the same/ Adding yet another thread to this doesn't help with anything.Daredevil
Thanks for this @Ferri - it helped meHartzog
C
0

The German call to TestMethod1 is failing, which causes the line that resets the culture to never get executed. Put that line in a finally block and the tests should execute as expected:

var originalCulture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE");
try
{
    TestMethod1();
}
finally
{
    Thread.CurrentThread.CurrentCulture = originalCulture;
}
Cordie answered 3/9, 2015 at 21:31 Comment(6)
You definitely need the try/finally mentioned in the answer to ensure the culture is reset, but I think you may still have a problem with tests running with an unexpected culture. MSTest runs tests in parallel and I am pretty sure it doesn't start a new thread for each test, but instead has some way to dispatch multiple tests on one thread. I guess you will have to try the try/finally and see if your problem goes away, but if it does not, I think what I mentioned will be the source of your problem. As far as I know, there is no way to force MSTest to run the tests sequentially.Crosscurrent
I believe MSTest only runs in parallel if you explicitly enable it, but the point is taken that this makes the error prone. It would probably be best to spin off another thread to do this work so it is isolated and controlled.Cordie
I think that link contains old information from VS2010. In VS2013 (and assumably VS2015) it runs tests in parallel by default. I'm not sure the config settings event still exist.Crosscurrent
As far as I know, the settings file may be different, but the limitation of parallel tests in MSTest is still present. There are comments from August of this year on this uservoice request: visualstudio.uservoice.com/forums/121579-visual-studio/…Cordie
The try/finally is what I needed, thanks. As for running tasks in parallel, I shouldn't need to worry about tests run on other threads because I'm only setting the culture for the current thread. Even if tests were parallel & weren't run with one thread per test (1 thread per core, typically), I would still expect each thread to be running just one test at any given time. Given the amount of work it would be to invent some sort of sub-thread scheduling to interleave multiple tests on a thread, and the number of tests likely to break if they did that, I'm not expecting Microsoft to do that.Cathartic
@Cathartic this will work even if tests are run in parallel. The wrapper and the inner test method run on the same thread.Daredevil

© 2022 - 2024 — McMap. All rights reserved.