How to unit test localization that is based on setting Thread.CurrentCulture in middleware?

144 views Asked by At

As per title, I'm working on a codebase that supports localization for several cultures. I rely on setting culture as follows in middleware early in the request pipeline:

string lang = "fr-FR";
Thread.CurrentThread.CurrentCulture = new CultureInfo(lang);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang);

I deployed it manually on a test server and tested it manually and it seems to work. However, I want to write some unit tests/end-to-end tests (whatever makes more sense), to validate this.

First off, I'm facing some problems in changing CurrentThread.CurrentCulture in the unit test environment. Moreover I'm unsure if mocking does really make a database makes sense as or really does test the localization.

Sample Test approach that I'm currently considering:

public class LocalizationTest
{
    private readonly Mock<IMenuService> _menuService;
    private readonly Mock<IDiscountService> _discountService;        
    private readonly Mock<IConfiguration> _configuration;


    public LocalizationTest()
    {
        _menuService = new Mock<IMenuService>();
        _discountService = new Mock<IDiscountService>();
        _configuration = new Mock<IConfiguration>();
    }

    [Fact]
    public async Task Should_Return_Valid_Menu()
    {
        //Arrange
        _menuService.Setup(x => x.GetItemAsync(It.IsAny<int>(), It.IsAny<int[]>()
            , It.IsAny<string>(), It.IsAny<bool>()))
            .ReturnsAsync(() => LocalizedMenuItemChoice(Thread.CurrentThread.CurrentUICulture));

        _configuration.SetupGet(x => x[It.IsAny<string>()]).Returns("Mocked Value");

        ConfigurationHelper.Initialize(_configuration.Object);

        var menuLogic = new MenuLogic(_menuService.Object);
        


        var requestModel = new GetMenuItemChoicesRequestModel()
        {
            someData = 1,
            someDataId = 2,
            someDataList = new int[1] { 3 },
            someDataNum = 4,
        };

        string lang = "fr-France"
        Thread.CurrentThread.CurrentCulture = new CultureInfo(lang);
        Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang);
        //Act
        var response = await menuLogic.GetItemsChoicesAsyncV2(requestModel);

        //Assert
        Assert.Equal(JsonConvert.SerializeObject(LocalizedMenuItemChoice(Thread.CurrentThread.CurrentCulture)) , JsonConvert.SerializeObject(response));

    }


    private ItemsList LocalizedMenuItemChoice(CultureInfo cultureInfo)
    {
        // Example: Switching between English and French data
        if (cultureInfo.Name == "fr-FR")
        {
            return new ItemList() {
                //fr-FR DATA
            };
        }
        else
        {
            return new ItemList() {
                //en-US DATA
            };
        }
    }


}
1

There are 1 answers

0
JonasH On

I would probably not write automated tests that use the actual responses from a webpage, since that would likely break the test even for valid and intended changes. And automated tests are much less valuable if you need to spend as much time updating old tests as you do implementing features.

In my experience the most common problems with translations are:

  1. Not all translation strings exist in all languages
  2. Translation strings that exist, but are not translated.
  3. Strings containing formatting symbols are managed in a translation
  4. Literal strings are used instead of translated strings
  5. Translation is just wrong

You can write automated tests for the first three points fairly easily. But you do not need to actually change the language, just iterate over all the strings in the translation files. But you might need a list of exceptions to handle point 3, since some strings are identical in multiple languages.

Point 4 is not very effective to unit test in my experience, since it is mostly caused by just forgetting to move a literal, and you will most likely not remember to write a unit test if that is the case. I have seen various approaches to minimize this risk:

  1. Code reviews
  2. Make literal strings easier to spot when doing manual testing. For example by
    1. Write all literals in ALL CAPS or [inside tags].
    2. Create a special translation where all words are replaced by "smurf".

Note that you will need to do some manual testing of each translation anyway. If for no other reason than to check that the UI does not look weird due to string or word length, (looking at you Finland!)

For the final point you just need someone you trust that knows the language and the domain. If an external translator makes a bad translation you have little chance to catch the error if you have no one that understands the language.