Can't access information from configuration files when tests have host type "Moles"
Asked Answered
H

3

3

We are having problems accessing information in .net configuration files (such as app.config and web.config) via unit tests when the unit tests have a host type of "Moles". It's causing quite a few headaches, so I hope someone has an idea on what can be done.

We're using Visual Studio 2010, and I believe we've tried this on a machine with VS 2010 SP1 installed, and machine without SP1 installed, as well as trying it on 32 bit and 64 bit machines.

I've taken the liberty of reducing the test to its simplest terms. The problem can be recreated by composing a unit testing project consisting of the following two files, and running the test after uncommenting the only commented line. The test works without a host type, but when you introduce Moles as a host type, the null assertion in the test fails. We are not sure why.

First, the configuration file App.config:

<?xml version="1.0"?>
<configuration>
  <connectionStrings>
    <add name="Connection" connectionString="Something" />
  </connectionStrings>
</configuration>

Next, the test class containing a single test:

namespace TestProject
    {
    using System.Configuration;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class UnitTest
        {

        [TestMethod]
        //[HostType("Moles")]
        public void TestMethod()
            {
            var data = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            Assert.IsNotNull(data.ConnectionStrings.ConnectionStrings["Connection"]);
            }

        }

    }

I would be grateful if anyone could offer any insight.

Thanks very much,

Nick

Higginson answered 2/2, 2012 at 17:47 Comment(2)
possible duplicate of How To Read UnitTest Project's App.Config From Test With HostType("Moles")Mizuki
Also see my similar question at #8859587Sailmaker
R
1

I'm not sure if it will do the job, but you can try this workaround: open the config using a file mapping. The code will look like this:

ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = configurationFilePath;
System.Configuration.Configuration configuration =
    ConfigurationManager.OpenMappedExeConfiguration(
        fileMap,
        ConfigurationUserLevel.None);
Rumilly answered 3/2, 2012 at 19:36 Comment(0)
C
1

Any time you are performing unit tests, application and user settings should be passed via dependency injection. This is accomplished by creating a Stub for the settings, which is easy to do.

  1. Create an interface, in the project being tested, that includes a property for every configuration setting. Lets call this "ISettings," for reference.

  2. Create a stub (class) in the target assembly that implements the interface. Each property in the stub should contain only a get that returns the corresponding setting form the configuration file. We'll refer to this stub as "SettingsStub". This stub is used by the target assembly, in the production environment.

  3. Add an ISettings typed argument to the target type (class being tested) constructor. A field in the target class must be set to the ISettings object passed to the constructor. You may create an overload constructor, and preserve the default constructor, as required by some design patterns (MVVM, etc.). The default constructor (the one that has no arguments) may simply instantiate a new SettingsStub, for use in production. The overloaded constructor must always be used by tests!

  4. Create a settings stub to the test project, that also implements ISettings. We'll refer to this as TestSettingsStub. This stub contains hard-coded values that are acceptable for most tests.

  5. Rebuild the target and test projects. Moles generates a Stub type named SISettings.

Use the concrete TestSrtyingsStub, when you don't need to adjust any of the setting values. Alternatively, use the Miles Stub type, when the values need to be adjusted for one or two tests. The purpose of the Moles Stub types is to avoid the need to make many stubs that contain one or two unique changes.

The SettingsStub, TestSettigsStub, and SISettings types may be used interchangeably, when calling the overloaded constructor. Now, you have full control over what settings are used in each context, without the need for switching logic or manually changing setting values, during testing. The target code simply retrieves setting values from the local field, instead of directly from the configuration file. Please refer to Dependency Injection and Inversion of Control (IOC) topics.

As usual, your development workstation development not be able to access external dependency systems (database, etc.) on the production network, for safety!

Happy coding!

Calk answered 4/2, 2012 at 19:30 Comment(5)
Downvoting because while point 1 is good, anything beyond that isn't. Moles is WELL in excess for that kind of approach and isn't in any way what it was intended for. A simple mocking framework such as Moq will suffice in that case. Moles should be used for code that you can't mock, e.g. file system access, database calls, etc... that are going in unit tests rather than integration tests.Inquisitorial
@Inquisitorial I do not advocate using a mocking framework for a simple case of dependency injection. However, there is nothing wrong with using stubbing features of a mocking framework already in use.Calk
@Inquisitorial I don't see how using Moles is a poor choice for use in tests. It is, after all, a mocking and stubbing framework. I'd even say it's easier to use that Moq, particularly for small projects; because, writing a detour via a single lambda is faster than building a Moq object, and achieves the same ends. When one already employs Moles, why also use Moq, when they both do the same job?Calk
It's a horrible choice if it's code that you have control over. Configuration and injecting it into a class is something you have control over. Simulating FileSystem access is something you do not have control over and is where Moles is appropriate. If you're using Moles for any code that you've written, it's indicative of a bad design.Inquisitorial
@Inquisitorial Ooh! Very good comment. Up vote!! I am going to bronze that, and hang it in my office, for the juniors to see on a daily basis. (Seriously!)Calk
I
0

I'd agree that Mike's answer is logically correct (i.e. you're not separating your configuration loading from the class - potentially), the practical matter is that for Moles host types, per your original question, you'd need to Mole the calls to the configuration system, e.g.

 MConfigurationManager.AllInstances.OpenExeConfiguration (... finish your moleing here...)

The syntax is approximate - I can't remember if you end up with an SConfigurationManager or MConfigurationManager in that case.

Where I wholly disagree with Mike is that the statement "...development workstation not be able to access external dependency systems..." is flat out horrible advice. We make these things called integration tests.

Yes, you as a developer should be creating them. At some point you will write code that touches a concrete implementation (e.g. a database, backing service, etc...) and if you're not testing that interaction, you're pretty much doing it wrong.

Inquisitorial answered 6/2, 2012 at 6:19 Comment(3)
Unit tests are supposed to only test the targeted code, and nothing else. Integration tests are certainly supposed to test interaction with other code and external resources (I'm not an idiot). I was under the impression this is a question regarding unit testing, and not integration testing. If this is a question of unit testing, then yours would be the "flat out horrible advice." Unit and integration tests are mutually exclusive. Also, there is no need to shoot others down, when you are equally capable of dispensing bad advice.Calk
Please read my reply. It seems we both need to actually read each other's more carefully. I made a clear distinction between unit tests and integration tests. My error was in reading the "...production network" portion of your advice. Largely irrelevant to the conversation, and a potentially overloaded term, but I'll go out on a limb and assume you're simply aiming for production SORs.Inquisitorial
I concur. Context is a rather sly detriment to communication, between programmers. Well played. I tip my hat to you, good sir.Calk

© 2022 - 2024 — McMap. All rights reserved.