模拟Regex.IsMatch()

时间:2014-05-08 14:24:54

标签: c# visual-studio oop unit-testing mocking

所以,我有自己的Luhn算法实现,并且我使用正则表达式来验证用户的输入。

我继续进行单元测试,我发现自己遇到了这个问题:

Mock<Regex> regexMock = new Mock<Regex>();
regexMock.Setup(r => r.IsMatch(It.IsAny<string>())).Returns(true);

注意: 我使用Moq框架进行模拟

但不知何故,最后一行代码抛出异常

  

非虚拟(VB中可覆盖)成员的设置无效:r =&gt; r.IsMatch(It.IsAny&LT;串GT;())

我想知道我有什么替代方法可以解决我的嘲弄问题,或者可以采取一些可行的方法。

提前致谢!

修改

好的,所以我的测试看起来像这样:

Mock<Regex> regexMock = new Mock<Regex>();
regexMock.Setup(r => r.IsMatch(It.IsAny<string>())).Returns(true);

Mock<IRegexBuilder> builderMock = new Mock<IRegexBuilder>();
builderMock.Setup(m => m.Build()).Returns(regexMock.Object);

LuhnAlgorithm luhn = new LuhnAlgorithm(builderMock.Object);

string input = "7992739871";
ushort expected = 3;

object output = luhn.GenerateChecksum(input);

Assert.IsInstanceOfType(output, typeof(ushort));
Assert.AreEqual(expected, (ushort)output);

我有这个IRegexBuilder这是我为帮助创建正则表达式而创建的另一个类。重要的是最终的Regex对象是通过调用IRegexBuilder.Build()方法完成的。

现在,我可以模拟该方法并返回一个固定的正则表达式,如:

builderMock.Setup(m => m.Build()).Returns(new Regex("\d+"));

但我不想在测试中定义我的验证。

伙计们,我希望我的验证(但是它已经完成)不影响我的测试,我想模拟输入匹配以返回true或false,与验证的方式无关。如果我在测试方法中创建自己的正则表达式,那么每当我将来更改验证逻辑时,我都必须更改测试。

3 个答案:

答案 0 :(得分:4)

为什么要模拟正则表达式?

虽然正则表达式是Luhn实现的内部依赖项,但它不是应该注入的依赖项,因此不应该被模拟。

如果执行Luhn检查是对验证代码的依赖,以及验证它确实执行Luhn检查的内容,则可以使用接口/抽象类,其中实现可以在内部执行regex。

可能是

interface ICardValidator
{
    bool IsCardValid(string cardNumber);
}

class LuhnCardValidator : ICardValidator
{
    private static readonly Regex _cardRegex = new Regex(...);

    bool IsCardValid(string cardNumber)
    {
        return Regex.IsMatch(cardNumber);
    }
}

您可以针对LuhnCardValidator编写单元测试,以验证您的Luhn检查是否有效。

[Test]
[TestCase("4242424242424242")
public void ShouldBeValid(string cardNumber)
{
    Assert.IsTrue(new LuhnCardValidator().IsCardValid(cardNumber));
}

您还可以针对依赖于ICardValidator的代码编写测试,例如,当验证失败时,它会向用户显示相应的错误消息。

[Test]
public void ShouldPresentCardFailedMessage()
{
    var mockCardValidator = new Mock<ICardValidator>();
    mockCardValidator.Setup(x => x.IsCardValid(It.IsAny<string>()).Returns(false);

    var validationSummary = new ValidationSummary(mockCardValidator.Object);

    validationSummary.ValidateThePage(...);

    var errors = validationSummary.GetErrors();

    Assert.IsTrue(errors.Any(x => x.Message == "Credit card number is not valid"));
}

答案 1 :(得分:2)

如果正则表达式代表一个复杂的算法,你可以将匹配封装在一个带接口的自定义类中,并依赖注入(或模拟它)。

如果是基本验证,我根本不会嘲笑它。你会嘲笑String.Contains()吗?

答案 2 :(得分:0)

当您使用Moq创建模拟时,它会创建实现您正在模拟的接口的类,或者从您正在模拟的类继承的类。这个生成的类应该提供自己实现的模拟成员。界面简单,因为没有实现。类成员应该是抽象的或虚拟的。 Regex.IsMatch不是抽象的,也不是虚拟的。因此Moq无法创建自己的成员实现以进行设置。

你应该使用Moq可以处理的东西。通常一些包装类具有虚方法或实现一些接口。该类应将工作委托给Regex class:

public interface IWrapper // use appropriate name of interface
{
    bool IsValid(string value)
}

此界面可以轻松模拟。您现在可以为使用此接口的客户端编写测试。对于现实生活,您需要实现将工作委托给Regex

public class Wrapper : IWrapper
{
   public bool IsValid(string value)
   {
       // use Regex here
   }
}