Mocking local variables within the method-under-test

3 posts, 0 answers
  1. Asim
    Asim avatar
    7 posts
    Member since:
    Jul 2011

    Posted 26 Jul 2011 Link to this post

    Hi!

    Suppose my method-under-test looks like this:

    public void TestMe()
    {
        DataSet d1 = null;
        DataSet d2 = null;
        bool process = false;
        Helper helper = new Helper();
        process = helper.LoadFromDB(out d1, out d2) && d1 != null && d1.Tables[0] != null && d1.Tables[0].Count > 0

        if (process)
        {
            [...run business logic here...]
        }
        else
        {
            throw new Exception("Failed to load data from the db");
        }
    }

    In order to circumvent using a physical database, I mock "helper.LoadFromDB" in my unit test. The delegate I use in the "DoInstead" method populates the test1DS and test2DS datasets with made-up data, and the "Returns" in my arrange returns true.

     


    Mock
    .Arrange(() =>  helper.LoadFromDB(out test1DS, out test2DS))

    .DoInstead(someDelegate)

    .Returns(true);

     
    The problem is that the data sets the mock implementation populates are the ones declared within the unit test, not the ones declared in the method-under-test. So the process flag in the code snippet above still returns false (due to d1 being null), and the business logic I wanted to test (within the "if (process)" block above) never gets run.

    Is there a way to map (for lack of a better word) the test1DS and test2DS (that I declared in the unit test code and populated in my DoInstead delegate) to the data sets d1 and d2 declared within the method-under-test, so as to allow the process flag to evaluate to true once the mocked implementation has run?

    Thanks,
    Asim

  2. Asim
    Asim avatar
    7 posts
    Member since:
    Jul 2011

    Posted 28 Jul 2011 Link to this post

    Update: I managed to find a solution to the issue in the original message. The trick was to not use a DoInstead in my Arrange statement. Instead, I pulled all the logic that I had originally put into the body of my DoInstead delegate into the body of the unit test method itself, and just used the Returns call in the Arrange, minus any DoInstead, like so:

     

    Mock.Arrange(() => helper.LoadFromDB(out testDS1, out testDS2)).Returns(true);

     
    After that, the " && d1 != null && d1.Tables[0] != null && d1.Tables[0].Count > 0" test (in the method-under-test) did pass.

    This raises a very interesting question: Why is it that when I put the logic to instantiate and populate out parameters testDS1 and testDS2 in the body of the unit test method itself, the mocking works fine, but when I put the logic to instantiate and populate out parameters testDS1 and testDS2 in the Action delegate used in the "DoInstead", the method-under-test (that invokes the method that was mocked) sees these out parameters as null (i.e., un-initialized)? Is it a bug or by design?

    Thanks,
    Asim

  3. DevCraft R3 2016 release webinar banner
  4. Ricky
    Admin
    Ricky avatar
    467 posts

    Posted 01 Aug 2011 Link to this post

    Hi Asim,

    Thanks again for bringing the question. Out parameter is set as expected value which is basically a result value to be assigned once the method is executed. Therefore during Mock.Arrange, you are specifying to return the  expected out value once the method is executed rather what is set from the method itself.

    In that case the following setup is specifying to set testDS1 and testDS2 for the out params and return the expected true value as well.

    Mock.Arrange(() => helper.LoadFromDB(out testDS1, out testDS2)).Returns(true

    While this is much cleaner than creating an extra Delegate and moving your test code there from the test method. However DoInstead is intended to perform tasks based on input (Action delegate). This is a similar behavior can be found with other mocking tools therefore keeps the consistency as well.

    Hope that answers your question.

    Kind regards,
    Mehfuz
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get now >>

Back to Top