About virtual method

21 views
Skip to first unread message

Hui Zhao

unread,
Nov 1, 2015, 9:41:14 AM11/1/15
to Moq Discussions
I am not sure that I understand the virtual correctly.
Mock<ILine> mockLine = new Mock<ILine>();
mockLine.Setup(x => x.Hangup());
 // ASSERT
mockLine.Verify();
My Hungup  methos is:
public void Hangup() { }
There is no 'Virtual' there. But in some examples, I saw there is `Virtual`. 

What is the difference?
Thanks 
 

Ale Miralles

unread,
Nov 2, 2015, 8:05:33 AM11/2/15
to moq...@googlegroups.com
Moq works with proxies. The only way it can change the actual implementation of a method is by overriding it. That's why you need that virtual modifier.

~ Ale Miralles


 

--
--
Post: moq...@googlegroups.com
Unsubscribe: moqdisc-u...@googlegroups.com
---
You received this message because you are subscribed to the Google Groups "Moq Discussions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to moqdisc+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Hui Zhao

unread,
Nov 2, 2015, 8:09:59 AM11/2/15
to Moq Discussions
Thanks, but you misunderstood my question.

In my example, I don't have virtual modifier at all. It is still working, why?

Ale Miralles

unread,
Nov 2, 2015, 8:32:03 AM11/2/15
to moq...@googlegroups.com
Oh, my bad. You can try something like this then:

using Moq;

public interface ILine {
void Hangup();
}

public class Program {
static int Main(){
var mockline = new Mock<ILine>();
mockline.Setup(m => m.Hangup());

// Verifies nothing (And of course, it passes).
//mockline.Verify();

// Verifies that the method was called only once.
// mockline.Verify(m => m.Hangup(), Times.Once());
//
// So:
// mockline.Object.Hangup();
mockline.Verify(m => m.Hangup(), Times.Once()); //Will fail unless you uncomment the previous line.


return 0;
}
}

~ Ale Miralles

Hui Zhao

unread,
Nov 2, 2015, 8:47:09 AM11/2/15
to Moq Discussions
My code:
 public void Hangup_Should_Execute()
        {
            // ARRANGE
            Mock<ILine> mockLine = new Mock<ILine>();

            // ACT
            mockLine.Setup(x => x.Hangup());
            mockLine.Object.RunCallScript();
            // ASSERT
            mockLine.Verify();
        }
There is no "Virtual" on the method, why it is working. 

Vicenç Garcia

unread,
Nov 2, 2015, 8:59:18 AM11/2/15
to moq...@googlegroups.com
Hi Hui,

your code should have this kind of structure: 
---------------------
| SUT                |
|--------------------|
| - collaborator  |
|____________|

So, your test should act on the SUT and check if the collaborator has been called.

In your code, you are acting directly on the collaborator, which is not correct.

Hope it helps,
Vicenc

Ale Miralles

unread,
Nov 2, 2015, 9:08:26 AM11/2/15
to moq...@googlegroups.com
For sure, you _should_ change the last line to look something like this (assuming the method should be called only once):

mockLine.Verify(m => m.Hangup(), Times.Once()); 

And for what I'm seeing in your code:
mockLine.Object.RunCallScript();

I assume you wanna verify that any call to RunCallScript will internally end up calling Hangup, right?
If so, your code is not going to work, because you are performing the invocation on the "proxied" object, not on the actual thing. Is that's the case, you better take a look at dependency injection first, and then revisit your tests.

Probably my guessing is wrong (I don't know your particular project) but it is a pretty common mistake when people start using mocks.



Hui Zhao

unread,
Nov 2, 2015, 9:11:28 AM11/2/15
to Moq Discussions
That is true. I want any call to RunCallScript will internally end up calling Hangup. So can I use your code in the previous reply?

Ale Miralles

unread,
Nov 2, 2015, 9:32:27 AM11/2/15
to moq...@googlegroups.com
Not really, it was just an example to show you how verify works. 
What you need to do, as Vicenc pointed out, is refactor your code to a "collaborators" model or something like that.
In this particular case, you will need at least two different classes, one of them could be mocked and injected into the other.
Give me a couple of hours and I'll post a demo project online. I guess it will be easy to review code/concepts by taking a look at an actual implementation.

Ale Miralles

unread,
Nov 2, 2015, 10:03:22 AM11/2/15
to moq...@googlegroups.com
Ok, here is an example.
Suppose you have an order processor that calls an external service for credit card verification. (This is a super naive approach, but it proves the point).
This code will verify that the processing pipeline performs the call to the credit card verification service when processing the order.

using Moq;

public interface ICreditCardService { //<= The dependency.
bool IsValidCreditCard(int cardNumer);
}

public class OrderProcessor {
readonly ICreditCardService _creditCardService;

public OrderProcessor(ICreditCardService creditCardService) { // <= Injection happens here.
_creditCardService = creditCardService;
}

public void ProcessOrder(int cardNumber, object order) {
// Uncomment this line, and the assertion will fail.
// return;
if (_creditCardService.IsValidCreditCard(cardNumber)) {
//TODO: Do the actual processing.
}
//TODO: Reject the order.
}
}

public class Program {
static int Main() {
//Arrange
var mockedSvc  =  new Mock<ICreditCardService>();
mockedSvc.Setup(ms => ms.IsValidCreditCard(It.IsAny<int>())).Returns(true);
var orderProcessor = new OrderProcessor(mockedSvc.Object); //<= Dependency Injection.
//Act
orderProcessor.ProcessOrder(123, new { /* What ever the order is */ });

//Assert
mockedSvc.Verify(ms => ms.IsValidCreditCard(It.IsAny<int>()), Times.Once());

return 0;
}

}


I think you will have no trouble porting this example to your own project.
If you hace any question, please lemme know.

~Ale Miralles.

Hui Zhao

unread,
Nov 2, 2015, 10:28:34 AM11/2/15
to Moq Discussions
The difficult thing is that in my implementation, I don't have the constructor injection.
Let's say the interface is:
 public interface ILine
    {
        Guid LineId { get; set; }
        void RunCallScript();
        void Hangup();
    }
And in the implementation, 
    public class VoiceElementsLine : ILine
    {
        public Guid LineId { get; set; }
public void RunCallScript()
        {
            SetupLine();
            AnswerSipChannel();
            var inboundValues = _inboundDnis.Split('_');
            var callFlowApp = GetCallFlowApplication();
            callFlowApp.CallInfo = new VoiceElementsCallInfo
            {
                LanguageCode = inboundValues[1],
                FacilityId = inboundValues[2],
                InmateId = inboundValues[3]
            };
            Language lan = (Language)Enum.Parse(typeof(Language), callFlowApp.CallInfo.LanguageCode);
            LangInfo = LanguageData.GetLanguage(lan);
            callFlowApp.LineApi = this;
            callFlowApp.HandleCall();
            Hangup();
        }
May I and how to verify Hangup is called?

Thanks for your time. 

Vicenç Garcia

unread,
Nov 2, 2015, 11:12:42 AM11/2/15
to moq...@googlegroups.com
Hi Hui,

with your current implementation you can't. You need to redesign your code.

Cheers,

Ale Miralles

unread,
Nov 2, 2015, 11:27:38 AM11/2/15
to moq...@googlegroups.com
No problem.

I think all goes down to two choices:

The first one, is refactor your code. I strongly recommend you go that route, because testing/mocking/stubbing will be a lot easier. In my experience, if tests are hard to write/read people stop doing them and that's a recipe for disaster.

The second one, is a little dirty/hackie trick (IMHO) and consist in mark the Hangup method as virtual (so it can be mocked) and create a mock of the VoiceElementsLine class. Since RunCallScript is not virtual, even the mocked version of VoiceElementsLine will end up calling the real implementation and you still can assert/verify on the mocked object that its version of the Hangup method was called. (But again, this is a non starter to me, because the code will be messy and in the future even the people who wrote it will have a hard time trying to figure out was going on)

~ Ale Miralles.


Reply all
Reply to author
Forward
0 new messages