- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我被指派去帮助遗留系统;该系统在整个应用程序中都引用了第三方库(超过 2000 次)。应用程序中没有单个单元测试,这是一个关键任务系统。
我想做的是重构代码,这样第三方类就不会在整个应用程序中被引用。我还想围绕我可以控制的代码引入单元测试。
使用第 3 方 dll 的代码示例如下所示(第 3 方类是 Controller 和 Tag):
public class Processor
{
private Controller _clx;
private Tag _tag;
public bool Connect(string ip, string slot, int timeout, bool blockWrites, string tagName)
{
_clx = new Controller(ip, slot, timeout);
if (_clx != null)
{
_clx.Connect();
if (_clx.IsConnected)
{
if (tagName != null)
{
_tag = new Tag(_clx, tagName, ATOMIC.DINT);
return ((_tag.ErrorCode == 0) && _tag.Controller.IsConnected);
}
_tag = new Tag {Controller = _clx, DataType = ATOMIC.DINT, Length = 1};
return (_tag.Controller.IsConnected);
}
}
return false;
}
}
我已经能够创建一个包装器类来帮助我删除对第 3 方 dll 的所有引用,现在所有调用都通过我的包装器,而我的包装器是唯一调用第 3 方 dll 的代码。
public class ControllerWrapper
{
public ControllerWrapper(string ip, string slot, int timeout)
{
Controller = new Controller(ip,slot,timeout);
}
public Controller Controller { get; set; }
}
public class TagWrapper
{
public Tag Tag { get; set; }
public TagWrapper()
{
}
public TagWrapper(ControllerWrapper clx, string tagName, ATOMIC datatype)
{
Tag = new Tag(clx.Controller, tagName,datatype);
}
}
现在我的 Processor 类看起来像:
public class Processor
{
private ControllerWrapper _clx;
private TagWrapper _tag;
public bool Connect(string ip, string slot, int timeout, bool blockWrites, string tagName)
{
_clx = new ControllerWrapper(ip, slot, timeout);
if (_clx != null)
{
_clx.Controller.Connect();
if (_clx.Controller.IsConnected)
{
if (tagName != null)
{
_tag = new TagWrapper(_clx, tagName, ATOMIC.DINT);
return ((_tag.Tag.ErrorCode == 0) && _tag.Tag.Controller.IsConnected);
}
_tag = new TagWrapper {Tag = {Controller = _clx.Controller, DataType = ATOMIC.DINT, Length = 1}};
return (_tag.Tag.Controller.IsConnected);
}
}
return false;
}
}
我的问题是我仍然无法对 Processor.Connect(...) 进行单元测试
附加信息——
我想我不明白的是如何从 Connect 方法中创建标签和 Controller ,这样我就可以在单元测试中拥有假标签和假 Controller ,但拥有真正的标签和 Controller 在生产代码中。
我已经玩了大约 4 天了,实现了很多不同的版本,但仍然不知所措。
我试过如下的构建器类:
public static class TagBuilder
{
public static ITagProxy BuildTag()
{
return new TagProxy().CreateTag();
}
public static ITagProxy BuildTag(IControllerProxy clx, string tagName, ATOMIC datatype)
{
return new TagProxy().CreateTag(clx, tagName, datatype);
}
}
使用类似 ITagProxy 的东西
public interface ITagProxy
{
Tag Tag { get; set; }
ITagProxy CreateTag();
ITagProxy CreateTag(IControllerProxy clx, string tagName, ATOMIC dataType);
}
和 ControllerProxy 类似:
public interface IControllerProxy
{
Controller Controller { get; set; }
IControllerProxy CreateController(string ip, string slot, int timeout );
}
现在处理器代码如下所示:
public class Processor
{
private IControllerProxy _clx;
private ITagProxy _tag;
public virtual bool Connect(string ip, string slot, int timeout, bool blockWrites, string tagName)
{
_clx = CreateController(ip, slot, timeout);
if (_clx != null && _clx.Controller != null)
{
_clx.Controller.Connect();
if (_clx.Controller.IsConnected)
{
// check this connection carefully, if it fails the controller is not in the slot configured
if (tagName != null)
{
// use supplied Tag Name to to a qualified connection
_tag = TagBuilder.BuildTag(_clx, tagName, ATOMIC.DINT);
return ((_tag.Tag.ErrorCode == 0) && _tag.Tag.Controller.IsConnected);
}
_tag = TagBuilder.BuildTag();
_tag.Tag.Controller = _clx.Controller;
_tag.Tag.Length = 1;
return (_tag.Tag.Controller.IsConnected);
}
}
return false;
}
public virtual IControllerProxy CreateController(string ip, string slot, int timeout)
{
if (_clx == null)
_clx = new ControllerProxy();
return _clx.CreateController(ip, slot, timeout);
}
}
但还是要依赖具体的Tag和Controller。
如何让代码不依赖于具体标签和 Controller ?
最佳答案
除了包装 Controller
和 Tag
类之外,您还需要一种方法来创建不直接公开第三方的包装类的实例动态链接库。这通常使用 Abstract Factory pattern 完成。 ,它允许您同时拥有一个具体的工厂类(创建第三方 DLL 对象和关联包装器)和模拟/ stub 工厂对象(为单元测试创建包装器)。
由于您显然没有可用的 DI/IOC 工具,您将需要一些其他方法来设置 Processor
的工厂对象以用于测试目的。一种方法是使工厂对象成为 Processor
类的公共(public)成员;另一种方法是使其成为 Processor
的 protected 成员,并将 Processor
子类化以用于测试目的。无论哪种方式,使用延迟初始化的属性访问工厂可确保代码默认使用“真实”代码。
public interface IControllerProxy
{
public bool IsConnected { get; }
public void Connect();
}
public interface ITagProxy
{
public IControllerProxy Controller { get; }
public int Length { get; set; }
public int ErrorCode { get; }
}
public interface IProxyFactory
{
IControllerProxy CreateControllerProxy(string ip, string slot, int timeout);
ITagProxy CreateTagProxy(IControllerProxy clx, string tagName, WrappedClasses.ATOMIC dataType);
ITagProxy CreateTagWrapper(IControllerProxy clx, WrappedClasses.ATOMIC dataType, int length);
}
private class ConcreteControllerProxy : IControllerProxy
{
private WrappedClasses.Controller _clx;
public ConcreteControllerProxy(string ip, string slot, int timeout)
{
_clx = new WrappedClasses.Controller(ip, slot, timeout);
}
public bool IsConnected
{
get { return _clx.IsConnected; }
}
public void Connect()
{
_clx.Connect();
}
public WrappedClasses.Controller Controller { get { return _clx; } }
}
private class ConcreteTagProxy : ITagProxy
{
private WrappedClasses.Tag _tag;
public ConcreteTagProxy(WrappedClasses.Tag tag)
{
_tag = tag;
}
public ConcreteTagProxy(WrappedClasses.Controller clx, string tagName, WrappedClasses.ATOMIC dataType)
{
_tag = new WrappedClasses.Tag(clx, tagName, dataType);
}
}
public class ConcreteProxyFactory : IProxyFactory
{
public IControllerProxy CreateControllerProxy(string ip, string slot, int timeout)
{
return new ConcreteControllerProxy(ip, slot, timeout);
}
public ITagProxy CreateTagProxy(IControllerProxy clx, string tagName, WrappedClasses.ATOMIC dataType)
{
ConcreteControllerProxy controllerWrapper = clx as ConcreteControllerProxy;
return new ConcreteTagProxy(controllerWrapper.Controller, tagName, dataType);
}
public ITagProxy CreateTagWrapper(IControllerProxy clx, WrappedClasses.ATOMIC dataType, int length)
{
ConcreteControllerProxy controllerWrapper = clx as ConcreteControllerProxy;
WrappedClasses.Tag tag = new WrappedClasses.Tag
{
Controller = controllerWrapper.Controller,
DataType = dataType,
Length = length
};
return new ConcreteTagProxy(tag);
}
}
public class Processor
{
protected IProxyFactory _factory;
private IProxyFactory Factory
{
get
{
if (_factory == null )
{
_factory = new ConcreteProxyFactory();
}
return _factory;
}
}
private IControllerProxy _clx;
private ITagProxy _tag;
public bool Connect(string ip, string slot, int timeout, bool blockWrites, string tagName)
{
_clx = Factory.CreateControllerProxy(ip, slot, timeout);
if (_clx != null)
{
_clx.Connect();
if (_clx.IsConnected)
{
if (tagName != null)
{
_tag = Factory.CreateTagProxy(_clx, tagName, WrappedClasses.ATOMIC.DINT);
return ((_tag.ErrorCode == 0) && _tag.Controller.IsConnected);
}
_tag = Factory.CreateTagWrapper(_clx, WrappedClasses.ATOMIC.DINT, 1);
return (_tag.Controller.IsConnected);
}
}
return false;
}
}
// This class would be in your test suite
public class TestableProcessor : Processor
{
public IProxyFactory Factory
{
get { return this._factory; }
set { this._factory = value; }
}
}
关于c# - 围绕第三方 dll 创建包装器以抽象 dll 并能够对我的代码进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25628441/
这是一种复杂的情况。我正在重构(从头开始)c++,它必须用作 CGI 脚本和独立应用程序的核心。 遗憾的是,我从大学开始就没有写过C++,对c#/Java比较熟悉。所以我打算将 WPF 用于 GUI。
您好,我正在尝试找出与此线程中提出的问题相同的问题 How to use CSS to surround a number with a circle? 但是 - 每次我这样做时,形状都会变成椭圆形,
如果您在单个语句中执行某些操作,例如“abc”+ stringval +“abc”,那么是一个不可变的字符串副本,还是两个(注意 abc 和 123 在编译时是常量) 奖励回合:使用像下面这样的 St
我正在尝试创建一个查询,该查询只会在满足某些条件的情况下添加 AND 子句。 这就是我所追求的: SELECT DISTINCT id name active FROM team WHER
在使用 Google 的出色绘图工具进行了一些试验后,我正在使用 Gnuplot 绘制几个 3D 图形。我喜欢 Google 工具的一件事是它在表面周围绘制的“边界框”,这让我更容易看到大小。 有没有
我们最近从solr迁移到 Elasticsearch 。 因此决定以自定义查询格式编写一个包装器,该包装器将转换为 Elasticsearch 查询。将来,如果我们更改为另一个数据存储,则只需要修改此
我有以下内容将音频剪辑的频率绘制为条形音箱: const drawSinewave = function() { requestAnimationFrame(drawSinewave);
我试图围绕其父矩形的中心旋转一个矩形。 child 到 parent 边界的距离必须始终保持不变。我几乎成功了,但我的方法似乎有一个小错误。我似乎找不到问题所在。 示例: http://jsfiddl
我有一个帮助类来将用户对象保存到共享首选项。我用过 serialize(): String函数和 create(serializedString: String)我的 User 中的函数数据模型。他们
是否可以围绕 UIBezierPath 的可见部分绘制路径? 这是我的问题的一个例子 这是我想要完成的 这是我到目前为止得到的: - (void)drawRect:(CGRect)rect { C
这里,AsciiChecker启用文本形式的矩阵规范。 abstract class AsciiChecker extends AlgoritmicChecker { String[] asc
目前,我有十个不同的查询,它们通过 JDBC 处理,并包装在返回 ResultSet 的函数中。这些 ResultSet 对象中的每一个都由外部程序进行迭代,并将通过其索引而不是根据要求的列名进行访问
围绕 finder 方法启动事务是否明智: @Transactional public E getParticularEvent(final String id) { return (E)em
我需要一个围绕 Canvas 边缘移动的圆圈。向右然后向下移动可以正常工作,但是当它需要向左移动时,它会跳到右下角并开始一次又一次地向右移动。我不知道如何解决这个问题。 var can = doc
我正在尝试我的第一个 jQuery 插件。 (耶……时间到了!) 我很难思考如何让一个可公开访问的函数正常启动。 代码 (function($, doc, win){ "use strict"
在阅读了很多关于绕相机旋转的指南并询问了一些关于 SO 的其他问题后,我想到了 SSCCE我到目前为止所拥有的。也许这样其他人会更容易理解我需要什么,对我来说答案是什么。 到目前为止它看起来像这样:
这里是 Java 菜鸟!我正在努力为我正在编写的 Android 应用程序画龙点睛。本质上,它是一个 RSS 阅读器。异步任务获取 RSS 提要。然后对其进行解析,我想做的最后一点是使用已解析的 RS
我有以下代码,从数据库的“类(class)”表中选择标题和图像。 setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
我正在尝试实现一个表盘,其中一只手的位图图像围绕 Canvas 上的表盘中心旋转。 基本上在 onDraw() 方法中,我希望能够将图像资源放到 Canvas 上,然后每秒旋转一次。 我有每秒触发一次
我从 SwingX 找到了一个名为 JXLoginPane 的组件在 WindowBuilder 中可用,这似乎是我尝试做的事情的一个很好的起点,但我需要有关如何使用它的更多信息。到目前为止,我发现唯
我是一名优秀的程序员,十分优秀!