假设我有这个接口和这个类:
class readFile {
public fun getBuffer(sigId: String): ArrayList<DoubleArray> {
val address="/data/data/com.example.amin.ecgs/Signals/Sig$sigId.bin"
lateinit var buf: DoubleBuffer
try {
val rFile = RandomAccessFile(address, "rw")
val inChannel = rFile.channel
val buf_in = ByteBuffer.allocate(8 * 12 * 1002)
buf_in.clear()
inChannel.read(buf_in, 0)
buf_in.rewind()
buf = buf_in.asDoubleBuffer()
inChannel.close()
} catch (e: IOException) {
e.printStackTrace()
}
return generateData(buf)
}
private fun generateData(buf: DoubleBuffer): ArrayList<DoubleArray> {
val arrayList = ArrayList<DoubleArray>(12)
for (n in 0..11) {
val yb = DoubleArray(1002)
buf.get(yb, n * 1002, (n + 1) * 1002)
arrayList.add(yb)
}
buf.clear()
return arrayList
}
}
所以我想知道我是否可以从GetScheduler方法获取模拟实例。我试过这样的事情:
public interface IScheduler
{
void StopTimer();
// some other methods
}
public class Scheduler : IScheduler
{
private static readonly IScheduler scheduler = new Scheduler();
private readonly Timer timer;
public Scheduler()
{
refreshTimer = new Timer
{
Enabled = false,
AutoReset = false
};
}
public static IScheduler GetScheduler()
{
return scheduler;
}
public void StopTimer()
{
timer.Stop();
}
// some other methods
}
但没有用,它说“System.ArgumentException:对象实例不是由Moq创建的。”
请问任何建议?
在命令类中有这样的东西:
[TestMethod]
public void Execute_ButtonClicked_StopTimer()
{
// arrange
var mockScheduler = Mock.Get(Scheduler.GetScheduler());
var command = GetCommandInstance();
// act
command.Execute();
// assert
mockScheduler.Verify(m => m.StopTimer());
}
答案 0 :(得分:4)
我提出了一种不同的方法,可以避免这样做......
Scheduler类正在实现单例模式来控制其构造。你需要能够抽象出依赖于IScheduler
的东西而不是它的构造方式。所以 else 应该负责管理调度程序的构造:它本身不应该这样做,因为构造不是该类的责任(single responsibility principle)。
常见的方法是使用Gang of of Four Factory method pattern或service locator pattern(例如Microsoft的UnityContainer)。这些中的任何一个都可以被指示将该类暴露为单例,使该类只是该类负责的实现。
Dependency Injection完成了拼图,因为当类注入了它们的依赖项时,它们本身就会从它们使用的东西的构造中抽象出来。因此,需要IScheduler
的类会注入一个并使用它。
有了这些模式,在问题中做出请求的必要性就会消失,并导致代码明确地分离出来。
脚注:我知道这些模式看起来令人生畏,看起来它似乎为此做了很多努力,而且很难看到它的好处;但请相信我:尝试这个(我真的是说尝试一下,你不能只是半心半意地尝试,因为它在方法上是一个很大的改变)。我曾经像你发布的那样编写代码。我被建议看看这些模式。我有严重的疑虑,但我从未回头,现在我以这种方式编写所有代码。
答案 1 :(得分:0)
如果您在虚拟呼叫中包裹您要替换的呼叫,则可以这样做:
public class Scheduler : IScheduler
{
private static readonly IScheduler scheduler = CreateScheduler();
private readonly Timer timer;
public Scheduler()
{
refreshTimer = new Timer
{
Enabled = false,
AutoReset = false
};
}
public static IScheduler GetScheduler()
{
return scheduler;
}
public void StopTimer()
{
timer.Stop();
}
public virtual IScheduler CreateScheduler()
{
return new Scheduler();
}
// some other methods
}
然后在您的单元测试课程中:
[TestClass]
public class TestClass
{
[TestMethod]
public void testMethod()
{
var scheduler = new Mock<Scheduler>();
// you can now call .setup on the scheduler instance and use this mock.
var sut = new SchedulerWrapper(scheduler.Object);
///var sut....sometests
}
}
internal class SchedulerWrapper : Scheduler
{
private Scheduler _scheduler;
public SchedulerWrapper(Scheduler scheduler)
{
_scheduler;
}
public overrride IScheduler CreateScheduler()
{
return _scheduler;
}
}
答案 2 :(得分:0)
目前还不清楚你要通过模拟测试什么。
如果测试需要检查私有静态字段,那么利用反射将是可行的方法,但最终会得到与Scheduler.GetScheduler()
相同的结果。
从类型中获取私有静态字段的基本方法是:
var field = typeof(MyType).GetField( "MethodName", BindingFlags.NonPublic | BindingFlags.Static);
var fieldValue = field.GetValue(myTypeInstance);
模拟对虚拟地址表起作用(在运行时,MSIL检查增值税以查找要运行的方法的地址)。抽象基础和虚拟实例方法是可模拟的,接口也是如此。
如果虚拟添加到实例中StopTimer
的签名中,它将是Mockable,这是从Mock<IScheduler>
获得的所有内容。