3

When we unit test we often need to change some Sitecore configuration for the tests. For example, we may have the following:

<sitecore>
  <configuration>
    <someNode name="myService">http://myservice:1234/endpoint</someNode>
    <settings>
      <setting name="mySftp" value="sftp://mysftp:21/" />
    </settings>
  </configuration>
</sitecore>

and we may need to change it to the following when running our unit tests:

<sitecore>
  <configuration>
    <someNode name="myservice">http://test.myservice:1234/endpoint</someNode>
    <settings>
      <setting name="mySftp" value="sftp://test.mysftp:21/" />
    </settings>
  </configuration>
</sitecore>

I had previously been handling these changes by using different build configurations with config transforms. The problem with this is that I prefer to run my unit tests across both DEBUG and RELEASE mode, since the optimizations when building in RELEASE mode (shouldn't but) can cause errors in some code. I could work around this by customizing my targets and making two build configurations for unit tests, unitdebug and unitrelease, but this seems kind of hacky and I'm sure there are better solutions out there.

I'm wondering what techniques other people are using for handling these changes when running unit tests.

Note: Partial answers are totally acceptable for this one. I expect that there may be different techniques for different kinds of settings. Getting a bunch of ideas together that I and others can pick and choose from will make a great reference for the community! Of course, I will accept the answer that works best in the most cases :)

2
  • 1
    For simple settings you can use fakeDb.Configuration.Settings["SettingName"] = "NewValue"; in test code. Commented Nov 30, 2016 at 15:22
  • Add that as an answer so we can upvote :) Commented Nov 30, 2016 at 15:25

4 Answers 4

4

Assuming that you are using FakeDB for your Unit Tests you can simply change settings on the fly.

See example:

using (Db fakeDb = new Db())
{
    fakeDb.Configuration.Settings["SettingName"] = "SettingValue";
}

More info here.

1
  • Very cool! I haven't actually started using fakeDb in my unit tests, but I have been reading up on it and learning what I can so that I can POC it before my next project. +1 Commented Nov 30, 2016 at 19:00
7

I abstract out the configuration in to one (usually one but could more if it's a large project) Class with a corresponding Interface. Then rather than changing the config - I inject a mocked Interface with the config defined so that the unit test tests the functionality I'm after.

E.g.

public class ModuleConfiguration : IModuleConfiguration
    {
        public string MySetting
        {
            get
            {
                return Settings.GetSetting("MySetting",
                    "MyDefaultValue");
            }
        }

I try my hardest not to muddy the water with Sitecore API functionality mixed in with other functional stuff so I'd have some sort of manager class to abstract away any Sitecore specific functionality and then probably not worry too much about Unit testing Sitecore API stuff specifically.

In the past, when I've wanted to do integration tests with specific Sitecore API functionality - I've used an approach similar to this one where you use app.config to define the configuration for the tests to run. That allows you to have separate config for your tests compared to your Website project.

1
  • I like it - that's pretty similar o what I have been using lately for Settings (I'll post in a minute). The approach you linked to is also interesting. I'm going to have to try that one. Thanks :) Commented Nov 30, 2016 at 15:33
2

Recently, I have started using the SettingsSwitcher for mocking Sitecore <setting> values. I configure all of the mocked values in the constructor of a TestRunner class that I initialize before running any unit tests. The class is an IDisposable that I dispose of after the tests complete.

Using the example in the OP, the setting[@name='mySftp'] has the value sftp://mysftp:21/ but when unit testing this value should be changed to sftp://test.mysftp:21/. In order to do this, I would do the following:

public class TestRunner : IDisposable
{
    private SettingsSwitcher _settingsSwitcher;    

    public TestRunner()
    {
        _settingsSwitcher = new SettingsSwitcher("mySftp", "sftp://test.mysftp:21/");

        // ...continue calling SettingsSwitcher constructor for additional settings...

        // note that you don't have to keep setting the value of _settingsSwitcher after it has been initialized once
        new SettingsSwitcher("settingName", "settingTestValue");
    }

    public void Dispose() 
    {
        if (_settingsSwitcher != null) 
        {
            _settingsSwitcher.Dispose();   
        }
    }
}  

Unfortunately, this approach doesn't work for other Sitecore config node types, including custom ones, but it works well for the settings.

2

In more recent releases of Sitecore (8.2 and later), settings are provided by an abstract class, Sitecore.Abstractions.BaseSettings, which is wired up to a DefaultSettings singleton via Sitecore's service registration. (Look at Sitecore.DependencyInjection. DefaultSitecoreServicesConfigurator if you want to see how Sitecore wires this up.)

Because of this, if your code can take advantage of constructor injection (e.g., it's an MVC controller or a pipeline processor and you add resolve="true" to the configuration) you can have your production code look like this:

private BaseSettings Settings { get; }

public MyClass(BaseSettings settings) {
     this.Settings = settings;
}

public SomeMethod() {

    string mySetting = Settings.GetSetting("mySetting"); 
    ...
}

Then in your test, you can use a tool like NSubstitue or Moq to give it the value you want:

 BaseSettings settings = Substitute.For<BaseSettings>();
 settings.GetSetting("mySetting").Returns("my test value");

 MyClass mc = new MyClass(settings);

If constructor injection is not available, you can use two constructors:

public MyClass(BaseSettings settings) {
     this.Settings = settings;
}

public MyClass(): this((BaseSettings)ServiceLocator.ServiceProvider.GetService(typeof(BaseSettings))){ }

If your configuration is stored as an object (see this and this), you will need to use BaseFactory.CreateObject("path"), following the same pattern. You can see all the "Base" abstractions by going to /admin/ShowServicesConfig.aspx. See also Sitecore's documentation.

Also worth noting that the old fashioned static methods are still there:

Sitecore.Configuration.Settings.GetSetting etc.

but these delegate their behavior to the DefaultSettings singleton, so you are completely safe referring to a BaseSettings local reference instead of the static method; they will point to the same singleton object when the code is run in production.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.