我使用NUNIT,主要是使用ValueSource测试来执行断言。
但我从不嘲笑。
我找到了一篇关于SO - 什么是嘲弄?的帖子
它说:
举个例子:您可以通过实现一个简单的内存中存储记录的结构来对数据库进行存根。然后,被测试对象可以将记录读写到数据库存根,从而允许它执行测试。这可以测试与数据库无关的对象的某些行为,并且只为让测试运行而包含数据库存根。 如果要验证被测试的对象是否将某些特定数据写入数据库,则必须模拟数据库。然后,您的测试将包含有关写入数据库模拟的内容的断言。
那么,模拟框架是否意味着作为一个用于读写的虚拟数据库来满足数据源的需求?
发布于 2011-12-02 15:40:41
不怎么有意思。模拟框架允许您生成模拟。模拟通常是一个对象,您可以将其转换为用于测试目的的具体实现。模拟将记录对其进行的调用,并且可以配置为在调用方法时返回特定值(或抛出异常)。通常只能为接口或抽象类创建模拟,因此为了获得模拟框架的好处,您的代码库必须遵循一些常见的OO最佳实践,例如:
编程到“接口”,而不是“实现”
没道理吗?让我们看看一个例子。
假设您有一个类,它获取一些网页并确定该页面是否包含单词"hello“。看起来可能是这样的:
public class HelloFinder
{
public HelloFinder()
{
}
public bool HasWord(string url)
{
var client = new WebClient();
try
{
var result = client.DownloadString(url);
return result.Contains("hello");
}
catch (Exception)
{
return false;
}
}
}那么,如果您的测试机器没有连接到internet,您将如何测试这个功能呢?您不能这样做,因为client.DownloadString总是抛出异常。对于需要数据库中的特定数据才能工作的其他类型的函数,情况也是一样的。它们不能使用模拟进行测试,因为它们不遵守“程序到接口而不是实现”的实践。那我们怎么才能改变呢?好的,我们可以编写提供HasWord方法的字符串的代码,以便搜索到HelloFinder类的依赖关系,这样我们就可以模拟它:
public interface IStringDownloader
{
string DownloadString(string url);
}
public class StringDownloader: IStringDownloader
{
public string DownloadString(string url)
{
var client = new WebClient();
return client.DownloadString(url);
}
}
public class HelloFinder
{
private readonly IStringDownloader downloader;
public HelloFinder(IStringDownloader downloader)
{
if (downloader == null) throw new ArgumentNullException("downloader");
this.downloader = downloader;
}
public bool HasWord(string url)
{
try
{
var result = this.downloader.DownloadString(url);
return result.Contains("hello");
}
catch (Exception)
{
return false;
}
}
}此代码与上面的示例完全相同,但它允许我们测试HasWord方法,而不必担心internet连接。通过使用模拟而不是实际的实现(StringDownloader)。
在您的实时应用程序中,代码可以这样使用:
var finder = new HelloFinder(new StringDownloader());
var googleHasHello = finder.HasWord("http://www.google.com");但是为了测试HasWords方法,我们并不关心字符串是来自google.com还是其他地方。我们只需测试给定的任何字符串它的行为是否正确,以及当下载程序抛出异常时,它的行为也是正确的。这就是模拟发挥作用的地方。就我个人而言,我使用名为莫克的模拟框架,所以这里的示例也使用这个框架。下面是一个使用模拟的测试:
public void TestThatFinderWillReturnTrueWhenHelloIsInString()
{
var downloaderMock = new Mock<IStringDownloader>();
downloaderMock.Setup(d => d.DownloadString(It.IsAny<string>())).Returns("A string that has hello in it");
var finder = new HelloFinder(downloaderMock.Object);
var result = finder.HasWord("any string will do");
//Assert that result is true
}这是怎么回事?首先,我们创建一个新的Mock,并要求它模拟IStringDownloader接口。然后,我们调用Setup方法告诉mock在每次调用DownloadString方法时返回一个硬编码字符串,我们还告诉它不要关心参数是什么(通过It.IsAny<string>()参数实现的)。现在我们有了一个模拟,它的行为是我们想要的,所以我们使用它来创建HelloFinder类并调用CountWords方法。最后,我们所要做的就是检查结果是否正确。
这个类的其他有用测试可能是:
public void TestThatFinderWillCallCorrectMethod()
{
var downloaderMock = new Mock<IStringDownloader>();
downloaderMock.Setup(d => d.DownloadString(It.IsAny<string>()))
.Returns("A string that has hello in it").Verifiable();
var finder = new HelloFinder(downloaderMock.Object);
var result = finder.HasWord("any string will do");
downloaderMock.Verify();
}
public void TestThatFinderWillReturnFalseWhenHelloIsNotInString()
{
var downloaderMock = new Mock<IStringDownloader>();
downloaderMock.Setup(d => d.DownloadString(It.IsAny<string>())).Returns("A string that does not have h3llo in it");
var finder = new HelloFinder(downloaderMock.Object);
var result = finder.HasWord("any string will do");
//Assert that result is false
}
public void TestThatFinderWillReturnFalseWhenDownloaderThrowsAnException()
{
var downloaderMock = new Mock<IStringDownloader>();
downloaderMock.Setup(d => d.DownloadString(It.IsAny<string>())).Throws(new Exception());
var finder = new HelloFinder(downloaderMock.Object);
var result = finder.HasWord("any string will do");
//Assert that result is false
}要而论之
回到你原来的问题。一个模拟框架是什么行为像一个虚拟数据库吗?不完全同意。但这是你想做的任何事。你需要嘲笑你的单元吗?当然,它们是有帮助的,但它们要求您的代码是以某种方式编写的。使用依赖注入的项目很容易用模拟进行测试。但是,由于我不知道您的特定代码库,所以很难说。你说你已经有一些单元测试了。必须重写这些代码才能使用模拟,而且您的整个代码库也可能需要重写,以便能够使用模拟进行测试。因此,对于您当前的项目来说,模拟是否是一个好主意由您来决定。但是,在未来的项目中,一定要记住它们。
我希望这能使我们对这个问题有所了解。
https://stackoverflow.com/questions/8357966
复制相似问题