Skip to main content

XUnit - Change culture during your test execution

Most of the applications I’m building should support multiple languages and cultures. This means that I also need to test my application code taking into account this into account.

In XUnit, there is no out-of-the-box way to change the culture of your unit test. However XUnit is easy to extend and you can even find a UseCultureAttribute example that exacty provides the functionality I need.

using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading;
using Xunit.Sdk;
/// <summary>
/// Apply this attribute to your test method to replace the
/// <see cref="Thread.CurrentThread" /> <see cref="CultureInfo.CurrentCulture" /> and
/// <see cref="CultureInfo.CurrentUICulture" /> with another culture.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class UseCultureAttribute : BeforeAfterTestAttribute
{
readonly Lazy<CultureInfo> culture;
readonly Lazy<CultureInfo> uiCulture;
CultureInfo originalCulture;
CultureInfo originalUICulture;
/// <summary>
/// Replaces the culture and UI culture of the current thread with
/// <paramref name="culture" />
/// </summary>
/// <param name="culture">The name of the culture.</param>
/// <remarks>
/// <para>
/// This constructor overload uses <paramref name="culture" /> for both
/// <see cref="Culture" /> and <see cref="UICulture" />.
/// </para>
/// </remarks>
public UseCultureAttribute(string culture)
: this(culture, culture) { }
/// <summary>
/// Replaces the culture and UI culture of the current thread with
/// <paramref name="culture" /> and <paramref name="uiCulture" />
/// </summary>
/// <param name="culture">The name of the culture.</param>
/// <param name="uiCulture">The name of the UI culture.</param>
public UseCultureAttribute(string culture, string uiCulture)
{
this.culture = new Lazy<CultureInfo>(() => new CultureInfo(culture, false));
this.uiCulture = new Lazy<CultureInfo>(() => new CultureInfo(uiCulture, false));
}
/// <summary>
/// Gets the culture.
/// </summary>
public CultureInfo Culture { get { return culture.Value; } }
/// <summary>
/// Gets the UI culture.
/// </summary>
public CultureInfo UICulture { get { return uiCulture.Value; } }
/// <summary>
/// Stores the current <see cref="Thread.CurrentPrincipal" />
/// <see cref="CultureInfo.CurrentCulture" /> and <see cref="CultureInfo.CurrentUICulture" />
/// and replaces them with the new cultures defined in the constructor.
/// </summary>
/// <param name="methodUnderTest">The method under test</param>
public override void Before(MethodInfo methodUnderTest)
{
originalCulture = Thread.CurrentThread.CurrentCulture;
originalUICulture = Thread.CurrentThread.CurrentUICulture;
Thread.CurrentThread.CurrentCulture = Culture;
Thread.CurrentThread.CurrentUICulture = UICulture;
CultureInfo.CurrentCulture.ClearCachedData();
CultureInfo.CurrentUICulture.ClearCachedData();
}
/// <summary>
/// Restores the original <see cref="CultureInfo.CurrentCulture" /> and
/// <see cref="CultureInfo.CurrentUICulture" /> to <see cref="Thread.CurrentPrincipal" />
/// </summary>
/// <param name="methodUnderTest">The method under test</param>
public override void After(MethodInfo methodUnderTest)
{
Thread.CurrentThread.CurrentCulture = originalCulture;
Thread.CurrentThread.CurrentUICulture = originalUICulture;
CultureInfo.CurrentCulture.ClearCachedData();
CultureInfo.CurrentUICulture.ClearCachedData();
}
}

Here is an example where I used this attribute to test if my validation messages are translated correctly:

[UseCulture("nl-BE")]
[Fact]
public void Validator_TranslatesValidationMessages_Based_On_CurrentUICulture()
{
//Arrange
var invalidOrder = new Order();
invalidOrder.Orderlines.Add(new Orderline());
//Act
var validator = _fixture.Container.Resolve<IValidator>();
var result = validator.Validate(invalidOrder);
//Assert
Assert.False(result.IsValid);
Assert.Equal(5, result.ValidationErrors.Count);
Assert.Equal("'Id' mag niet leeg zijn.", result.ValidationErrors[0].ToString());
Assert.Equal("'Name' mag niet leeg zijn.", result.ValidationErrors[1].ToString());
Assert.Equal("'Email Address' mag niet leeg zijn.", result.ValidationErrors[2].ToString());
Assert.Equal("'Product' mag niet leeg zijn.", result.ValidationErrors[3].ToString());
Assert.Equal("'Amount' moet groter zijn dan of gelijk zijn aan '1'.", result.ValidationErrors[4].ToString());
}

Popular posts from this blog

Kubernetes–Limit your environmental impact

Reducing the carbon footprint and CO2 emission of our (cloud) workloads, is a responsibility of all of us. If you are running a Kubernetes cluster, have a look at Kube-Green . kube-green is a simple Kubernetes operator that automatically shuts down (some of) your pods when you don't need them. A single pod produces about 11 Kg CO2eq per year( here the calculation). Reason enough to give it a try! Installing kube-green in your cluster The easiest way to install the operator in your cluster is through kubectl. We first need to install a cert-manager: kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.5/cert-manager.yaml Remark: Wait a minute before you continue as it can take some time before the cert-manager is up & running inside your cluster. Now we can install the kube-green operator: kubectl apply -f https://github.com/kube-green/kube-green/releases/latest/download/kube-green.yaml Now in the namespace where we want t...

Azure DevOps/ GitHub emoji

I’m really bad at remembering emoji’s. So here is cheat sheet with all emoji’s that can be used in tools that support the github emoji markdown markup: All credits go to rcaviers who created this list.

DevToys–A swiss army knife for developers

As a developer there are a lot of small tasks you need to do as part of your coding, debugging and testing activities.  DevToys is an offline windows app that tries to help you with these tasks. Instead of using different websites you get a fully offline experience offering help for a large list of tasks. Many tools are available. Here is the current list: Converters JSON <> YAML Timestamp Number Base Cron Parser Encoders / Decoders HTML URL Base64 Text & Image GZip JWT Decoder Formatters JSON SQL XML Generators Hash (MD5, SHA1, SHA256, SHA512) UUID 1 and 4 Lorem Ipsum Checksum Text Escape / Unescape Inspector & Case Converter Regex Tester Text Comparer XML Validator Markdown Preview Graphic Col...