1

SITUATION

I have the following code here

ITest.cs

public interface ITest
{
    int Prop { get; }
    int Calc();
}

Test.cs

public class Test : ITest
{
    public int Prop { get; private set; }

    public int Calc()
    {
        return Prop * 2;
    }
}

And I want to test the Calc() method. If my understanding is correct You can NOT override the getter property of concrete classes without using the virtual keyword.

for example

var moq = new Mock<Test>();  // mocked Test class
moq.Setup(x => x.Prop).Returns(2); // throws an error, 
                                   // however with "virtual int Prop { get; }" it doesn't throw any exceptions
var moq = new Mock<ITest>();  // mocked ITest interface
moq.Setup(x => x.Prop).Returns(2); // works fine
                                   // but can not really test the Calc() method (it returns 0
                                   // because the proxy doesn't have Test's implementation)

QUESTION

so my question is: how can I test the functionality of Calc() without making virtual int Prop

btw... this doesn't work either

var moq = new Mock<ITest>();
moq.Setup(x => x.Prop).Returns(2);

var obj = (Test)moq.Object; // can NOT convert ITestProxy to Test
Community
  • 1
  • 1
  • 3
    What are you trying to achieve? I f you want to test the `Test` class, then _don't_ mock it. – Pac0 Apr 29 '20 at 09:05
  • 5
    @Hrant I can't get the point of your question. If you need to unit test the Calc method of the class Test, you do **NOT** need to mock anything. Test is a concrete class and you want to unit test one of its public methods, right ? So you simply need to instantiate the class call the method and write down some asserts. – Enrico Massone Apr 29 '20 at 09:05
  • You should use `SetupGet` for properties, not the `Setup` – Pavel Anikhouski Apr 29 '20 at 09:06
  • @Hrant you need to create a mock of the ITest interface **if and only if** you are testing a class which dependes on ITest. I mean a class, that at the moment does **not** appear in your code snippets, whose constructor **requires an instance of ITest**. In such a scneario a library like moq can help you to create a mock implementation of ITest. What is exactly your scenario ? – Enrico Massone Apr 29 '20 at 09:07
  • @Pac0 I want to override my property but it IS private so -> it's impossible to instantiate Test and write test.Prop = 3; –  Apr 29 '20 at 09:07
  • @PavelAnikhouski Already tried it... it throws the same error. –  Apr 29 '20 at 09:08
  • 1
    @EnricoMassone why that? It's extremely simple: I can NOT instantiate the Test class and override it's PROP in order to Test my CALC() method. –  Apr 29 '20 at 09:10
  • @Hrant you are right because the setter is private. But this doesn't mean that using a mock is the solution here. The point is that the class you have designed cannot be used because the return value of the Calc method will always be 0, because no client code is able to set the value of Prop, and nowhere in your class the value of Prop is modified. It seems to me a class design issue, not a mock issue. Mocking a concrete class does not make sense. – Enrico Massone Apr 29 '20 at 09:12
  • 1
    @Hrant you need to provide a way for class user to set the value of Prop. That's the only point. You do not need to mock anything. You need to change the class public surface so that someone using it is able to set a value for Prop. This does **not** necessarily mean to change the visibility of the setter to public. – Enrico Massone Apr 29 '20 at 09:14
  • @EnricoMassone I would argue on 'because no client code is able to set the value of Prop': It IS possible with IL/ILGenerator (call whatever You want)... I thought maybe the MOQ guys implemented such thing in their project. on 2nd statement I could say: why is this a designing issue? Why should I let someone to manipulate my property if I need it only in my class? –  Apr 29 '20 at 09:16
  • @Hrant If you were able to do what you want, then you wouldn't be testing your `Test`class, you'd be testing moq's ability to fake an interface. – GazTheDestroyer Apr 29 '20 at 09:21
  • @GazTheDestroyer I think that, 'testing moq's ability to fake' is a bit extreme: because I'm not faking here I'm just saying that my PROP could have different default values (which is not uncommon in production world)... but maybe I could be wrong here? Idk. –  Apr 29 '20 at 09:26
  • 2
    @Hrant I think you are misunderstand what moq does. Anything mocked is fake by definition. There is no real implementation behind it. It's a just a dumb implementation of an interface that returns what you tell it to return. Your code is not involved in any way. – GazTheDestroyer Apr 29 '20 at 09:34
  • @GazTheDestroyer maybe You are right... I somehow 'overrated' the MOQ framework. –  Apr 29 '20 at 09:38

4 Answers4

1

If you want to test Calc() then test Calc(). You do not need to mock anything

[TestMethod]
public void Test_Calc_DoublesProp()
{
    //arrange           
    var test = new Test(5);  //assumes Prop value can be injected in constructor

    //act
    int result = test.Calc();

    //assert
    Assert.IsTrue(result == 10); // 5 * 2
}
GazTheDestroyer
  • 20,722
  • 9
  • 70
  • 103
  • Again this doesn't fit the situation where I want to change my property only in my class... look here what I've just found: there is a moq framework that does that (unfortunately it is NOT free). https://stackoverflow.com/questions/11738102/how-to-mock-non-virtual-methods –  Apr 29 '20 at 09:34
1

Found a solution: Pose

It uses ILGenerator and is quite old, BUT it works.

example

var test = new Test(); // default value of 'Prop' is  0
var shim = Shim.Replace(() => test.Prop).With((Test @this) => // idk why he needs @this param
{
    return 100; // sets the 'Prop' value to 100
});

var result = 0;
PoseContext.Isolate(() =>
{
    result = test.Calc(); // prints 200;
}, shim);

  • 1
    That would be a solution to overcome the "private" issue. Note about the parameter : you don't need it, so you can probably replace `@this` by `_` or even replace the whole `(Test @this)` by `_` – Pac0 Apr 29 '20 at 12:01
  • @Pac0 I actually tried it before posting, but it throws an error ... idk why? Maybe a bug or something else. The project isn't being maintained so I could imagine, that it is a bug. –  Apr 29 '20 at 12:27
0

Mocks are to be used as replacement for dependencies around what you are trying to test.

Concerning your test, just test the actual exposed behavior of your class, meaning don't try to use private methods and setters. In any case, mokcing the class to test is not a valid approach.

For instance :

If you want to test the Test class :

[TestClass]
public class TestTests
{

    [TestMethod]
    public void Calc_WhenPropIsSetTo5_Returns10()
    {
        /**
         *   Seems this is what you want to test (see name of methods),
         *   and I don't think it is a good idea, as your setter is private.
         *   Since it's not a "public" behavior, you could either 
         *
         *    a) have the setter as internal for testing purposes,
         *
         *    b) (my recommendation) go for the two other tests below instead;
         *       this will actually test the exposed behavior of the class.
         *
         *    c) circumvent the accessibility (by using a library or using
         *       Reflection directly) to set the value of the prop. 
         *       See your own answer for an example. 
         */
    }

    [TestMethod]
    public void Calc_WhenCreatedWith5_Returns10()
    {
        // Arrange 
        var testSubject = new Test(5); // if the prop is set by constructor... otherwise arrange in the proper way.

        // Act
        var actualResult = testSubject.Calc();

        // Assert
        Assert.AreEqual(expected: 10, actualResult)
    }

    [TestMethod]
    public void Prop_WhenCreatedWith5_IsSetTo5()
    {
        // Arrange 
        var testSubject = new Test(5);

        // Act
        var actualResult = testSubject.Prop;

        // Assert
        Assert.AreEqual(expected: 5, actualResult)
    }
}

If you want to test something that uses ITest.Calc(),

then you should, in your actual code,

1 - depend on the interface ITest, not on implementation Test

2 - have the dependency injected to the class being tested, so that you can replace the usual implementation Test by Mock<ITest>.Object()

public class ClassThatUsesTest
{
    private readonly ITest _test;

    public ClassThatUsesTest(ITest test)
    {
        _test = test;    
    }


    public int SomeFunction()
    { 
       if (_test.Calc() == 10)
       {
           return 42;
       }
       else
       {
           return 0;
       }
    }
}

[TestClass]
public class ClassThatUsesTestTests
{
    [TestMethod]
    public void SomeFunction_WhenTestCalcReturns10_Returns42()
    {
        // Arrange
        var testMock = new Mock<ITest>();
        testMock.Setup(x => x.Calc()).Returns(10);
        var testSubject = new ClassThatUsesTest(testMock.Object);
        var expectedResult = 42;

        // Act
        var actualResult = testSubject.SomeFunction();

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

In the last example, you can see the mocks help us to isolate the test without depending on the actual implementation details of the Test class. Even if suddenly the actual Test is broken or change (e.g. you need to pass 6 instead of 5 to get 10), the test method doesn't care.

Pac0
  • 21,465
  • 8
  • 65
  • 74
-1

Though this feels not right as it changes method implementation for testing purpose, just for the sake of sharing:

public class Test : ITest
{
    private int _prop;

    public int Prop
    {
        get => ((ITest)this).Prop;
        private set => _prop = value;
    }

    public int Calc()
    {
        return Prop * 2;
    }

    int ITest.Prop => _prop;
}

In test:

var mock = new Mock<Test>();
mock.As<ITest>()
    .Setup(x => x.Prop)
    .Returns(3);

// This should return 6
mock.Object.Calc();
weichch
  • 9,306
  • 1
  • 13
  • 25
  • nice example, however I don't think that this is much more 'cleaner' than using reflections e.g. (PropertyInfo) –  Apr 29 '20 at 10:09
  • 1
    I agree :) It is just an *example* I guess. – weichch Apr 29 '20 at 10:10
  • 1
    @Hrant take a look at [`PrivateObject`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.testtools.unittesting.privateobject?view=mstest-net-1.2.0) if you are using MSTest. – weichch Apr 29 '20 at 10:12