3

My unit testing method is as follows

[Test]
public void TrackPublicationChangesOnCDSTest()
{
    //Arrange
    // objDiskDeliveryBO = new DiskDeliveryBO();            

    //Act
    var actualResult = objDiskDeliveryBO.TrackPublicationChangesOnCDS();

    //Assert 
    var expectedZipName = 0;
    Assert.AreEqual(expectedZipName, actualResult);
}

The Actual method TrackPublicationChangesOnCDS in BO is as follows

public int TrackPublicationChangesOnCDS()
{

    var resultFlag = -1;

    try
    {

        string pubUpdateFileCDSPath = CommonCalls.PubUpdateFileCDSPath;
        string pubUpdateFileLocalPath = CommonCalls.PubUpdateFileLocalPath;


        if (File.Exists(pubUpdateFileCDSPath))
            File.Copy(pubUpdateFileCDSPath, pubUpdateFileLocalPath, true);

        if (File.Exists(pubUpdateFileLocalPath))
        {

            string[] pubRecords = File.ReadAllLines(pubUpdateFileLocalPath);

            var pubRecordsExceptToday = pubRecords.Where(p => !p.Trim().EndsWith(DateTime.Now.ToString("dd/MM/yy"))).ToList();

            resultFlag = new DiskDeliveryDAO().TrackPublicationChangesOnCDS(pubRecordsExceptToday);

            File.WriteAllText(pubUpdateFileLocalPath, string.Empty);

            string[] pubRecordsCDS = File.ReadAllLines(pubUpdateFileCDSPath);
            var pubRecordsTodayCDS = pubRecordsCDS.Where(p => p.Trim().EndsWith(DateTime.Now.ToString("dd/MM/yy"))).ToList();
            File.WriteAllLines(pubUpdateFileCDSPath, pubRecordsTodayCDS);

        }

        return resultFlag;
    }
    catch (Exception)
    {

        return -1;
    }

}

I want to mock the method call DiskDeliveryDAO().TrackPublicationChangesOnCDS(pubRecordsExceptToday);

How Can I achieve this ? I am not getting enough examples online . I am using Moq library. Can someone help ?

Nkosi
  • 235,767
  • 35
  • 427
  • 472
Kuntady Nithesh
  • 11,371
  • 20
  • 63
  • 86
  • You want to be able to mock "as is" or you have some room to change the way your BO works? – DesertFox Apr 27 '17 at 05:28
  • It will be good if I mock as is . But some changes can be done . – Kuntady Nithesh Apr 27 '17 at 05:44
  • 1
    whenever you do new, its a hard dependency as you cannot inject this dependency. resultFlag = new DiskDeliveryDAO().TrackPublicationChangesOnCDS(pubRecordsExceptToday); you cannot really inject this dependency using MOQ. However you can do this using Microsoft fakes. – loneshark99 Apr 27 '17 at 05:44
  • @KuntadyNithesh You need to invert the tightly coupled dependency on `DiskDeliveryDAO` so that it can be replaced with a mock to allow more flexible unit testing in isolation and making code more maintainable. – Nkosi Apr 27 '17 at 09:43

3 Answers3

3

Current code is too tightly coupled to implementation concerns to make it easy to test in isolation.

Reviewing the method under test revealed the following dependencies that were abstracted

public interface ICommonCalls {
    string PubUpdateFileCDSPath { get; }
    string PubUpdateFileLocalPath { get; }
}

public interface IDiskDeliveryDAO {
    int TrackPublicationChangesOnCDS(List<string> pubRecordsExceptToday);
}

public interface IFileSystem {
    bool Exists(string path);
    void Copy(string sourceFilePath, string destinationFilePath, bool overwrite);
    string[] ReadAllLines(string path);
    void WriteAllText(string path, string contents);
    void WriteAllLines(string path, IEnumerable<string> contents);
}

Their respective implementations would wrap/implement the desired functionality in production.

The abstractions would now allow the method under test to be refactored as below.

public class DiskDeliveryBO {
    readonly ICommonCalls CommonCalls;
    readonly IDiskDeliveryDAO diskDeliveryDAO;
    readonly IFileSystem File;

    public DiskDeliveryBO(ICommonCalls CommonCalls, IDiskDeliveryDAO diskDeliveryDAO, IFileSystem File) {
        this.CommonCalls = CommonCalls;
        this.diskDeliveryDAO = diskDeliveryDAO;
        this.File = File;
    }

    public int TrackPublicationChangesOnCDS() {

        var resultFlag = -1;

        try {

            string pubUpdateFileCDSPath = CommonCalls.PubUpdateFileCDSPath;
            string pubUpdateFileLocalPath = CommonCalls.PubUpdateFileLocalPath;


            if (File.Exists(pubUpdateFileCDSPath))
                File.Copy(pubUpdateFileCDSPath, pubUpdateFileLocalPath, true);

            if (File.Exists(pubUpdateFileLocalPath)) {

                string[] pubRecords = File.ReadAllLines(pubUpdateFileLocalPath);

                var pubRecordsExceptToday = pubRecords.Where(p => !p.Trim().EndsWith(DateTime.Now.ToString("dd/MM/yy"))).ToList();

                resultFlag = diskDeliveryDAO.TrackPublicationChangesOnCDS(pubRecordsExceptToday);

                File.WriteAllText(pubUpdateFileLocalPath, string.Empty);

                string[] pubRecordsCDS = File.ReadAllLines(pubUpdateFileCDSPath);
                var pubRecordsTodayCDS = pubRecordsCDS.Where(p => p.Trim().EndsWith(DateTime.Now.ToString("dd/MM/yy"))).ToList();
                File.WriteAllLines(pubUpdateFileCDSPath, pubRecordsTodayCDS);
            }

            return resultFlag;
        } catch (Exception) {

            return -1;
        }
    }
}

Note how not much has actually changed in the method itself apart from changing the tight coupling of new DiskDeliveryDAO() to the inject dependency.

Now the class is more flexible and can be tested in isolation where you have complete control of the dependencies and their behavior.

public class DiskDeliveryBOTests {
    [Test]
    public void TrackPublicationChangesOnCDSTest() {
        //Arrange
        var expected = 0;
        var commonMock = new Mock<ICommonCalls>();
        //...Setup commonMock desired behavior

        var daoMock = new Mock<IDiskDeliveryDAO>();
        //...Setup daoMock desired behavior
        daoMock
            .Setup(_ => _.TrackPublicationChangesOnCDS(It.IsAny<List<string>>())
            .Returns(expected);

        var fileMock = new Mock<IFileSystem>();
        //...Setup fileMock desired behavior

        var objDiskDeliveryBO = new DiskDeliveryBO(commonMock.Object, daoMock.Object, fileMock.Object);

        //Act
        var actualResult = objDiskDeliveryBO.TrackPublicationChangesOnCDS();

        //Assert             
        Assert.AreEqual(expected, actualResult);
    }
}

Check Moq Quickstart for more on how to setup the desired behavior on the mocks.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • actualResult always returns 0 now . Even when my expected result is 1. What is the reson for it . – Kuntady Nithesh Apr 27 '17 at 18:57
  • 2
    @KuntadyNithesh, It will always return 0 (default int) if you do no setup the mock. setup the mock to do what you want. I was just demonstrating the test. How would I know what you want for your test. You have to set it up yourself. – Nkosi Apr 27 '17 at 19:37
  • Thank You very much . Successfully mocked . Along with your answer This video helped a lot https://youtu.be/Krj3-h198KQ . Adding here for anyone's reference in future – Kuntady Nithesh Apr 28 '17 at 04:04
1

It is not possible to mock DiskDeliveryDAO().TrackPublicationChangesOnCDS(pubRecordsExceptToday) using moq as it is directly newing the object using concrete class. It is possible only when we have the concrete class implement an interface and DiskDeliveryDAO object should be injected through DiskDeliveryBO's constructor.

0
var mockDeliveryDao = new Mock<DiskDeliveryDAO>();

mockDeliveryDao.Setup(o=>o.TrackPublicationChangesOnCDS(It.IsAny<List<string>>()).Returns(1);//or whatever flag you want

Now you need to pass the deliveryDao as a parameter to the constructor. Use Dependency Injection instead of creating a new DeliveryDao() object in the code.

That way your code will read something like:

    resultFlag = _diskDeliveryDao.TrackPublicationChangesOnCDS(pubRecordsExceptToday);

While your constructor would be setting the _diskDeliveryDao member variable.

arviman
  • 5,087
  • 41
  • 48