Search code examples
c#unit-testingmoqtypemock

Is Mocking able to replace functionality wrapped inside a method?


I'm trying to define a way to simulate an case on accessing into a database without accessing... That's probably sounds quite crazy, but it's not.

Here is an example about a method i would like to test:

    public IDevice GetDeviceFromRepository(string name)
    {
        IDevice device = null;
        IDbConnection connection = new SqlConnection(ConnectionString);
        connection.Open();
        try
        {
            IDbCommand command = connection.CreateCommand();
            command.CommandText = string.Format("SELECT DEVICE_ID,DEVICE_NAME FROM DEVICE WHERE DEVICE_NAME='{0}'", name);
            IDataReader dataReader = command.ExecuteReader();
            if(dataReader.NextResult())
            {
                device = new Device(dataReader.GetInt32(0),dataReader.GetString(1));
            }
        }
        finally
        {
            connection.Close();
        }
        return device;
    }

I'm pretending to mock IDataReader so i can control what's being read. Something like that (using Moq framework):

    [TestMethod()]
    public void GetDeviceFromRepositoryTest()
    {
        Mock<IDataReader> dataReaderMock = new Mock<IDataReader>();
        dataReaderMock.Setup(x => x.NextResult()).Returns(true);
        dataReaderMock.Setup(x => x.GetInt32(0)).Returns(000);
        dataReaderMock.Setup(x => x.GetString(1)).Returns("myName");
        Mock<IDbCommand> commandMock = new Mock<IDbCommand>();
        commandMock.Setup(x => x.ExecuteReader()).Returns(dataReaderMock.Object);
        Mock<RemoveDeviceManager> removeMock = new Mock<RemoveDeviceManager>();
        removeMock.Setup()
        RemoveDeviceManager target =new RemoveDeviceManager(new Device(000, "myName")); 
        string name = string.Empty; 
        IDevice expected = new Device(000, "myName"); // TODO: Initialize to an appropriate value
        IDevice actual;
        actual = target.GetDeviceFromRepository(name);
        Assert.AreEqual(expected.SerialNumber, actual.SerialNumber);
        Assert.AreEqual(expected.Name, actual.Name);
    }

My question is whether i can force method GetDeviceFromRepository to replace IDataReader by mocked one.


Solution

  • Although you're currently use Moq I think that the functionality you're looking for cannot be achived without dependency injection unless you use Typemock Isolator (Disclaimer - I worked at Typemock).

    Isolator has a feature called "future objects" that enable replacing a future instantiation of an object with a previously created fake object:

     // Create fake (stub/mock whateever) objects
     var fakeSqlConnection = Isolate.Fake.Instance<SqlConnection>();
     var fakeCommand = Isolate.Fake.Instance<SqlCommand>();
     Isolate.WhenCalled(() => fakeSqlConnection.CreateCommand()).WillReturn(fakeCommand);
    
     var fakeReader = Isolate.Fake.Instance<SqlDataReader>();
     Isolate.WhenCalled(() => fakeCommand.ExecuteReader()).WillReturn(fakeReader);
    
     // Next time SQLConnection is instantiated replace with our fake
     Isolate.Swap.NextInstance<SqlConnection>().With(fakeSqlConnection);