gpt4 book ai didi

c# - 围绕第三方 dll 创建包装器以抽象 dll 并能够对我的代码进行单元测试

转载 作者:太空狗 更新时间:2023-10-29 23:30:58 25 4
gpt4 key购买 nike

我被指派去帮助遗留系统;该系统在整个应用程序中都引用了第三方库(超过 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(...) 进行单元测试

附加信息——

  • 这是一个 winforms 应用程序 .NET 2.0。
  • 没有使用任何 DI 或 IOC 工具。
  • 具体的 Tag 对象需要具体的 Controller 对象。
  • 第 3 方 dll 无法在测试环境中使用,因为它使用 IP 并尝试连接到 Controller 。

我想我不明白的是如何从 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 ?

最佳答案

除了包装 ControllerTag 类之外,您还需要一种方法来创建不直接公开第三方的包装类的实例动态链接库。这通常使用 Abstract Factory pattern 完成。 ,它允许您同时拥有一个具体的工厂类(创建第三方 DLL 对象和关联包装器)和模拟/ stub 工厂对象(为单元测试创​​建包装器)。

由于您显然没有可用的 DI/IOC 工具,您将需要一些其他方法来设置 Processor 的工厂对象以用于测试目的。一种方法是使工厂对象成为 Processor 类的公共(public)成员;另一种方法是使其成为 Processorprotected 成员,并将 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com