- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我看到在 Art of Unit Testing 中提倡添加公共(public)属性(由 Roy Osherove)帮助找到并设置 SUT 使用的尴尬(或内部)协作者/协作,并且 我自己使用此技术取得了良好的效果。 (我也看到了使用额外构造函数的类似方法)
同样,测试隔离框架(例如 Moq)可以提供替代方案,并且使用 Moq a Callback 可用于帮助设置尴尬的协作者/协作。
我在这里经历的权衡是:
使用公共(public)字段将额外的项目引入到 SUT 中,测试代码稍微更简洁
对比
一个 SUT 没有被额外的项目弄得乱七八糟,使其可测试,并且测试代码稍微笨重(回调代码不是最漂亮的)。
在我的情况下,由于对什么是命令并且应该是查询的约束——一个可以返回 stub 数据的查询,没有简单的方法来管理 SUT 中的协作(没有上述机制)
SUT 中的协作者更新命令中通过引用传递的对象,这看起来像:(稍后在代码示例中重复)
var warehouse = new Warehouse(); // internal to Order
_repo.Load(warehouse); // Warehouse is filled by reference
编辑:我设计了一个有设计问题的例子——Warehouse 和 Order 过于亲密,应用程序服务可以用来协调交互等。问题的关键是我几乎无法控制 Warehouse 的获取方式人口稠密。我正在使用一个框架,该框架使用命令通过引用来混合对象。这就是问题所在,我知道,但不幸的是我被它限制了。所以这个问题的真正重点不是重新设计,而是纯粹是哪种方法、回调或公共(public)字段更可取,如果这是我们必须使用的全部。
下面的代码示例都是使用 Moq 和 NUnit 的工作示例。出于时间原因,我省略了添加应用程序服务来编排示例用例(基本上是从合规仓库填写订单 - 基于 Fowler 的 Mocks aren't Stubs 示例)。此外,这两种方法都采用经典方法进行单元测试,断言状态而不是验证行为,这不是我的问题重点。
在继续之前,我确实有一个偏好,但我很想看看其他人的建议或偏好。
所以首先是公共(public)属性方法、代码和测试:(聪明地使用 Func<>)
public class Order
{
private readonly IWarehouseRepo _repo;
public int Items { get; private set; }
public Func<Warehouse> WarehouseBuilder { get; set; }
public Order(IWarehouseRepo repo)
{
_repo = repo;
}
public void AddOrderItems(int numberOfItems)
{
var warehouse = WarehouseBuilder();
_repo.Load(warehouse);
warehouse.RemoveStock(numberOfItems);
Items += numberOfItems;
}
}
public class Warehouse
{
public int Items { get; set; }
public void RemoveStock(int numberOfItems)
{
Items -= numberOfItems;
}
}
[TestFixture]
public class Given_A_Warehouse_With_20_Items
{
private Order _order;
private Mock<IWarehouseRepo> _warehouseRepo;
private Warehouse _warehouse;
[SetUp]
public void When_An_Order_Is_Placed()
{
_warehouseRepo = new Mock<IWarehouseRepo>();
_warehouse = new Warehouse() { Items = 20 };
_order = new Order(_warehouseRepo.Object);
_order.WarehouseBuilder = () => _warehouse;
_order.AddOrderItems(5);
}
[Test]
public void Then_The_Order_Now_Has_5_Items()
{
Assert.That(_order.Items, Is.EqualTo(5));
}
[Test]
public void Then_The_Warehouse_Now_Has_15_Items()
{
Assert.That(_warehouse.Items, Is.EqualTo(15));
}
}
public interface IWarehouseRepo
{
void Load(Warehouse warehouse);
}
其次是回调方法、代码和测试:(回调中的智能)
public class Order
{
private readonly IWarehouseRepo _repo;
public int Items { get; private set; }
public Order(IWarehouseRepo repo)
{
_repo = repo;
}
public void AddOrderItems(int numberOfItems)
{
var warehouse = new Warehouse();
_repo.Load(warehouse);
warehouse.RemoveStock(numberOfItems);
Items += numberOfItems;
}
}
public class Warehouse
{
public int Items { get; set; }
public void RemoveStock(int numberOfItems)
{
Items -= numberOfItems;
}
}
[TestFixture]
public class Given_A_Warehouse_With_20_Items
{
private Order _order;
private Mock<IWarehouseRepo> _warehouseRepo;
private Warehouse _warehouse;
[SetUp]
public void When_An_Order_Is_Placed()
{
_warehouseRepo = new Mock<IWarehouseRepo>();
_warehouseRepo.Setup(repo => repo.Load(It.IsAny<Warehouse>())).Callback<Warehouse>(warehouseArgument =>
{
warehouseArgument.Items = 20;
_warehouse = warehouseArgument;
}
);
_order = new Order(_warehouseRepo.Object);
_order.AddOrderItems(5);
}
[Test]
public void Then_The_Order_Now_Has_5_Items()
{
Assert.That(_order.Items, Is.EqualTo(5));
}
[Test]
public void Then_The_Warehouse_Now_Has_15_Items()
{
Assert.That(_warehouse.Items, Is.EqualTo(15));
}
}
public interface IWarehouseRepo
{
void Load(Warehouse warehouse);
}
最佳答案
如果使用得当,添加公共(public)状态以使测试更容易是一种有效的技术。同样,面对复杂的测试,同时让生产代码保持完整也是完全有效的。两者都可能是错误的,所以第三种选择是也看看你的设计。实际上,您选择哪种取决于多种因素。提防任何说出唯一正确方法的人。
添加公共(public)状态很好,因为它很容易,但是很差,因为如果你不针对代码编写自动化测试,它就不会存在。当您掌握了一些遗留代码并且添加一些额外的字段不是什么大问题时,这通常是有意义的。如果您将这些设置为只读,您也可以限制这些字段的范围。有趣的是,在软件中,这种技术并没有得到应有的使用。在硬件、电路等领域中,实际装运时仍附有测试组件。这些在发货后从未使用过。
这往往是您看到的最常见的形式。杂乱或复杂的测试,无所不用其极以使测试正常工作。这里的好处是至少设计不会为了测试而与公共(public)领域妥协,但正如您所指出的那样,缺点是有些复杂和丑陋的测试。您可以通过使用 SUT 构建器或简单地重构您的测试来抵消这一点,例如提取帮助程序以隐藏更困惑的部分。如果这样做,您至少可以保留干净代码和更干净测试的好处。
可悲的是,最未充分利用但可以解决您的问题。测试难写?然后你的设计可以做改进。如果新代码的测试需要公共(public)状态以使其易于测试?然后你的设计可以做改进。
你的例子
在您的情况下,采取两害相权取其轻的做法是有意义的。使用回调的复杂测试是我个人的建议。只要你权衡以上的利弊。是的,测试看起来很复杂,并且由于您使用的工具有一些相当粗糙的语法,但这不是世界末日。
如果假设存在第三方依赖或其他原因,您确实无法更改加载仓库的方式,则还有另一种选择。创建您自己的仓库存储库,它将隐藏命令方面并返回一个新的仓库实例。您现在将有一个查询,然后您可以轻松地将其 stub 并避开上述问题。遗憾的是,这会引入一个新组件,因此您需要权衡一下。如果您可以将组件更改为查询,我建议您先这样做。
在我看来,Func
并不是真正的公共(public)状态。这不是一个领域。围绕设计错误的事实,这是一个相当新颖的 hack。
关于c# - 我应该使用回调还是引入公共(public)字段来帮助进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37690435/
我有这个问题: 我们声称对 float 使用相等测试是不安全的,因为算术运算会引入舍入错误,这意味着两个应该相等的数字实际上并不相等。 对于这个程序,您应该选择一个数字 N,并编写一个程序来显示 1
为什么这个脚本的输出是 5 而不是 8 ? 我认为 -- 意味着 -1 两次。 var x = 0; var y = 10; while ( x
我现在可以从 cmd 窗口中执行的 FFmpeg 过程中读取最后一行。 使用脚本主机模型对象引用此源。 Private Sub Command1_Click() Dim oExec
使用 vlookup,当匹配发生时,我想从匹配发生的同一行显示工作表 2 中 C 列的值。我想出的公式从 C 列表 2 中获取值,但它从公式粘贴在表 3 上的行中获取,而不是从匹配发生的位置获取。 这
我在破译 WCF 跟踪文件时遇到了问题,我希望有人能帮助我确定管道中的哪个位置发生了延迟。 “Processing Message XX”的跟踪如下所示,在事件边界和传输到“Process Actio
我有四个表,USER、CONTACT、CONACT_TYPE 和 USER_CONTACT USER_CONTACT 存储用户具有填充虚拟数据的表的所有联系人如下 用户表 USER_ID(int)|
以下有什么作用? public static function find_by_sql($sql="") { global $database; $result_set = $data
我正在解决 JavaBat 问题并且对我的逻辑感到困惑。 这是任务: Given a day of the week encoded as 0=Sun, 1=Mon, 2=Tue, ...6=Sat,
我正在研究一些 Scala 代码,发现这种方法让我感到困惑。在匹配语句中,sublist@ 是什么?构造?它包含什么样的值(value)?当我打印它时,它与 tail 没有区别,但如果我用尾部替换它,
我正在使用以下代码自行缩放图像。代码很好,图像缩放也没有问题。 UIImage *originImg = img; size = newSize; if (originImg.size.width >
Instruments 无法在我的 iPad 和 iPhone 上启动。两者都已正确配置,我可以毫无问题地从 xcode 调试它们上的代码,但 Instruments 无法启动。 我听到的只是一声嘟嘟
我想用 iPhone 的 NSRegularExpression 类解析此文本: Uploaded652.81 GB 用于摘录上传和652.81文本。 最佳答案 虽然我确实认为 xml 解析器更适合解
我找到了 solution在 Stackoverflow 上,根据过滤器显示 HTML“li”元素(请参阅附件)。本质上基于 HTML 元素中定义的 css 类,它填充您可以从中选择的下拉列表。 我想
这是一个简单的问题,但我是在 SQL 2005 中形成 XML 的新手,但是用于形成如下所示表中的 XML 的最佳 FOR XML SQL 语句是什么? Column1 Column2 -
我在 www.enigmafest.com 有一个网站!您可以尝试打开它!我面临的问题是,在预加载器完成后,主页会出现,但其他菜单仍然需要很长时间才能加载,而且声音也至少需要 5 分钟! :( 我怎样
好吧,我正在尝试用 Haskell 来理解 IO,我想我应该编写一个处理网页的简短小应用程序来完成它。我被绊倒的代码片段是(向 bobince 表示歉意,但公平地说,我并不想在这里解析 HTML,只是
如何使用背景页面来突出显示网站上的某个关键字,无论网站是什么(谷歌浏览器扩展)?没有弹出窗口或任何东西,它只是在某人正在查看的网站上编辑关键字。我以前见过这样的,就是不明白怎么做!谢谢你的帮助。 最佳
我是 Javascript 新手,需要一些帮助。 先看图片: . 积分预测器应用程序。 基本上当用户通过单选按钮选择获胜团队时它应该在积分栏中为获胜队添加 10 分,并且并根据得分高的球队自动对表格进
这是我的情况 - 我要发送一份时事通讯,我试图做的是,当用户单击电子邮件中的链接时,它会重定向到我的网页,然后会弹出一个灯箱,显示视频。我无法在页面加载时触发灯箱,因为您可以在查看灯箱之前转到同一页面
我有这个代码。 ¿Cuanto es ? Ir 我想获取用户输入的“验证码”值。我尝试这个但行不通。有什么帮助吗? var campo = d
我是一名优秀的程序员,十分优秀!