所以,我有自己的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,与验证的方式无关。如果我在测试方法中创建自己的正则表达式,那么每当我将来更改验证逻辑时,我都必须更改测试。
答案 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
}
}