1

I've tried following this as a guide for Mocking the registry: http://www.rhyous.com/2011/11/04/unit-testing-registry-access-with-rhinomocks-and-systemwrapper/

When I try to Mock it, I always get a null return for "reg" in my class when it tries to do the OpenSubKey call, in my _Real() test it works fine.

Tests:

private RegistryService CreateMockedRegistryService()
{
    var registryService = new RegistryService(MockRepository.GenerateMock<ILoggerFacadeExtended>(), MockRepository.GenerateMock<IConnectivityService>());
    // Mock the Base Key so we can throw errors and manipulate it
    registryService.ChangeBaseKey(MockRepository.GenerateMock<IRegistryKey>());
    return registryService;
}

private IRegistryKey CreateMockedHKLM()
{
    IRegistryKey hklmMock = MockRepository.GenerateMock<IRegistryKey>();
    hklmMock.Stub(x => x.Name).Return("HKEY_LOCAL_MACHINE");
    return hklmMock;
}

[TestMethod()]
public void GetRegistryKeyTest_Mocked()
{
    IRegistryService target = CreateMockedRegistryService();
    string machineName = "localhost";
    RegistryHive hive = RegistryHive.LocalMachine;
    string path = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
    string valueName = "SystemRoot";
    object defaultValue = @"C:\Windows";

    IRegistryKey keyMock = CreateMockedHKLM();
    keyMock.Stub(x => x.OpenRemoteBaseKey(hive, machineName)).Return(CreateMockedHKLM());
    var subKeyMock = MockRepository.GenerateMock<IRegistryKey>();
    subKeyMock.Stub(x => x.Name).Return(keyMock.Name + @"\" + path);
    subKeyMock.Stub(x => x.GetValueNames()).Return(new string[] { valueName });
    subKeyMock.Stub(x => x.GetValue(valueName)).Return(defaultValue);

    keyMock.Stub(x => x.OpenSubKey(path)).Return(subKeyMock);
    keyMock.Stub(x => x.GetValue(valueName)).Return(defaultValue);
    target.ChangeBaseKey(keyMock);

    IResult<object> expected = new Result<object>() { Results = defaultValue };
    IResult<object> actual;
    actual = target.GetRegistryKey(machineName, hive, path, valueName, defaultValue);

    Assert.AreEqual(expected.Results, actual.Results);
}

[TestMethod()]
public void GetRegistryKeyTest_Real()
{
    IRegistryService target = CreateMockedRegistryService();
    string machineName = "localhost";
    RegistryHive hive = RegistryHive.LocalMachine;
    string path = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
    string valueName = "SystemRoot";
    object defaultValue = @"C:\Windows";

    // We don't want to use the regular Mocked version but actually hit the registry so it works
    IRegistryKey baseKey = new RegistryWrap().LocalMachine;
    target.ChangeBaseKey(baseKey);

    IResult<object> expected = new Result<object>() { Results = defaultValue };
    IResult<object> actual;
    actual = target.GetRegistryKey(machineName, hive, path, valueName, defaultValue);

    Assert.AreEqual(expected.Results, actual.Results);
}

Actual Class:

private IRegistryKey _BaseKey;
public IRegistryKey BaseKey
{
    get
    {
        if (_BaseKey == null)
        {
            _BaseKey = new RegistryWrap().LocalMachine;
        }
        return _BaseKey;
    }
}

public void ChangeBaseKey(IRegistryKey inBaseKey)
{
    _BaseKey = inBaseKey;
}

public IResult<object> GetRegistryKey(string machineName, RegistryHive hive, string path, string valueName, object defaultValue)
    {
        return GetRegistryKey(BaseKey.OpenRemoteBaseKey(hive, machineName), path, valueName, defaultValue);
    }
}

public IResult<object> GetRegistryKey(IRegistryKey baseKey, string path, string valueName, object defaultValue)
{
    IResult<object> result = new Result<object>();
    try
    {
        if (baseKey == null) throw new ArgumentNullException("baseKey");
        if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("path");
        if (string.IsNullOrWhiteSpace(valueName)) throw new ArgumentException("valueName");
        var reg = baseKey.OpenSubKey(path);
        result.Results = reg.GetValue(valueName); // reg is null when I try to Mock it
     .....
    }
}
John
  • 6,503
  • 3
  • 37
  • 58

1 Answers1

0

I figured it out, I was adding the Stubs dealing with OpenSubKey to the initial keyMock and not the one that was returned by OpenRemoteBaseKey()

Here is the fixed code:

[TestMethod()]
public void GetRegistryKeyTest_Mocked()
{
    IRegistryService target = CreateMockedRegistryService();
    string machineName = "localhost";
    RegistryHive hive = RegistryHive.LocalMachine;
    string path = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
    string valueName = "SystemRoot";
    object defaultValue = @"C:\Windows";


    var keyMock = CreateMockedHKLM();
    var remoteKeyMock = CreateMockedHKLM();
    var subKeyMock = MockRepository.GenerateMock<IRegistryKey>();
    subKeyMock.Stub(x => x.Name).Return(keyMock.Name + @"\" + path);
    subKeyMock.Stub(x => x.GetValueNames()).Return(new string[] { valueName });
    subKeyMock.Stub(x => x.GetValue(valueName)).Return(defaultValue);
    remoteKeyMock.Stub(x => x.OpenSubKey(path)).Return(subKeyMock);
    remoteKeyMock.Stub(x => x.GetValue(valueName)).Return(defaultValue);

    keyMock.Stub(x => x.OpenRemoteBaseKey(hive, machineName)).Return(remoteKeyMock);
    target.ChangeBaseKey(keyMock);

    actual = target.GetRegistryKey(machineName, hive, path, valueName, defaultValue);

    Assert.AreEqual(expected.Results, actual.Results);
    subKeyMock.AssertWasCalled(x => x.GetValue(valueName));
}
John
  • 6,503
  • 3
  • 37
  • 58
  • 1
    Good. Now ask yourself what I am testing? It looks like you are testing a mock and not any implementation that would make it to production. Your test name even reflects it! If you would 'mock' the RepositoryService you would test if it operates on other objects (the mocks) as you would expect (e.g. does it call 'OpenRemoteBasekey'). You mostly seem to be stubbing it now and testing if the stub returns what you set it up to return. –  Feb 17 '12 at 00:19
  • 1
    @Tungano do you have a better way to Unit Test registry with out actually touching the registry? It still runs through my classes code and gives me the results I am looking for... isn't that the point of the Mocks and Stubs? I've been trying to get this to work all day so please let me know if you have a better way to do it :) – John Feb 17 '12 at 00:45
  • If you want to test some code that is dependent on RegistryService and avoid it touching the actual registry: Stub the GetRegistryKey methods. You can using a mocking framework to do this if both methods are virtual. Or create an IRegistryService and using your mocking framework to create a stub with that. Then make sure you can get the tested code to use your stub (dependency injection preferred). –  Feb 17 '12 at 00:52
  • If you really want to test the RegistryService: You can only prove it actually does what it should do by having it read an actually registry value. That's what it does. You can't test a hammer without hitting an actual nail. –  Feb 17 '12 at 00:54
  • @Tungano I am doing that as far as I can tell, and testing the Registry itself isn't going to work as the environment is different on my dev machine vs the live environment (eg remote registry is disabled, no network admin account). Testing the way I am I can make sure I handle both success and failures. – John Feb 17 '12 at 14:31
  • @Joppe if you do integration test, that is correct. If you do unit testing, then you should not be touching the system incl registry, file system, networking, database... So, yes, hammer/nail thingy is what applies to system/integration testing. Unit tests are what we use to test methods. That is what I learned so far :) – pixel Mar 23 '17 at 16:02