85

I have an interface defined as

interface IMath
{
 AddNumbersBetween(int lowerVal, int upperVal);
}

I can setup a basic Moq for the above as follows:

Mock<IMath> mock = new Mock<IMath>();    
mock.Setup(m => m.AddNumbersBetween(It.IsAny<int>(), It.IsAny<int>()));

call it

mock.Object.AddNumbersBetween(1,4);

and then verify that it was called

mock.Verify(m => m.AddNumbersBetween(1,4), Times.AtleastOnce());

I cant figure out how to setup the method AddNumbersBetween such that if the upperVal is lower than the lowerVal an exception is thrown

mock.Object.AddNumbersBetween(4,1);//should throw an exception

Essentially looking for something like:

mock.Setup(foo => foo.AddNumbersBetween("arg1 is higher than arg2")).Throws<ArgumentException>();
Raj Rao
  • 8,872
  • 12
  • 69
  • 83
  • 1
    I would recommend simply implementing the exception-throwing logic with a Callback, as that gives you full access to both parameters. As far as I know, the parameter matching clauses can only perform logic based on a single parameter. – Dan Bryant Nov 02 '10 at 19:07

2 Answers2

167

I know this is a year old, but I found a way to use multiple parameters with the latest version of Moq at least:

mock.Setup(x => x.Method(It.IsAny<int>(), It.IsAny<int>()))
    .Returns<int, int>((a, b) => a < b);
Peter
  • 37,042
  • 39
  • 142
  • 198
Gustyn
  • 2,024
  • 1
  • 15
  • 14
11

For single argument methods, the cleanest way would be:

mock.Setup(foo => foo.Submit(IsLarge())).Throws<ArgumentException>();
...
public string IsLarge() 
{ 
  return Match<string>.Create(s => !String.IsNullOrEmpty(s) && s.Length > 100);
}

This can't be applied if the method has several arguments. There is still a 'workaround' that could be used, that mimics what you want to achieve:

/// <summary>
/// Tests if a moq can send an exception with argument checking
///</summary>
[TestMethod]
public void TestException()
{
    Mock<IMath> mock = new Mock<IMath>();
    mock.Setup(m => m.AddNumbersBetween(It.IsAny<int>(), It.IsAny<int>()));

    mock.Setup(foo => foo.AddNumbersBetween(It.IsAny<int>(), It.IsAny<int>()))
        .Callback<int, int>((i, j) => CheckArgs(i, j));

    try
    {
        mock.Object.AddNumbersBetween(1, 2);
    }
    catch (Exception ex)
    {
        // Will not enter
        Console.WriteLine("Exception raised: {0}", ex);
    }
    try
    {
        mock.Object.AddNumbersBetween(2, 1);
    }
    catch (Exception ex)
    {
        // Will enter here, exception raised
        Console.WriteLine("Exception raised: {0}", ex);
    }
}

private bool CheckArgs(int i, int j)
{
    if( i > j)
        throw new ArgumentException();
    return true;
}
srodriguez
  • 1,937
  • 2
  • 24
  • 42
  • This works only on a single parameter and not multiple parameters – Raj Rao Nov 02 '10 at 20:48
  • My bad... there is another way of handling this, with a callback that will throw the exception if the arguments don't pass your validation. I've edited my answer with that method – srodriguez Nov 03 '10 at 08:58