Mock.Assert() 对具有不同参数值的顺序调用进行模拟时出现问题
Trouble with Mock.Assert() for sequential calls with different argument values to mock
有人可以看看下面的演示代码,让我知道我看到的是我的错误还是 Telerik 问题?
我使用的是 Telerik.JustMock v. 2014.1.1519.1。和 Microsoft.VisualStudio.QualityTools.UnitTestFramework 诉 10.0.0.0。
正如代码注释所指出的,当 id 变量相等时(对每个 id 调用一次),我得到了预期的结果,但当它们不同时却没有。当我逐步完成第一个测试时,我可以看到正在进行的预期调用,但 JustMock 然后告诉我它们没有进行。
我将不胜感激任何建设性的想法。希望这不是我睡眠不足的问题...
[TestClass]
public class RunnerTests
{
[TestMethod]
public void MakeTwoCallsDifferentIdsFails()
{
int idOne=1;
int idTwo=2;
DataTable dt=new DataTable();
dt.Columns.Add("Id");
dt.Rows.Add(idOne);
dt.Rows.Add(idTwo);
IProcessor mock = Mock.Create<IProcessor>();
Runner runner = new Runner(mock);
runner.Process(dt);
Mock.Assert(()=>mock.Process(Arg.IsAny<MyArgs>()), Occurs.Exactly(2));
//The following two asserts fail (with 0 calls made to mock), regardless of sequence:
Mock.Assert(()=>mock.Process(Arg.Matches<MyArgs>
(d=>d.Id==idOne)),Occurs.Once());
Mock.Assert(()=>mock.Process(Arg.Matches<MyArgs>
(d=>d.Id==idTwo)),Occurs.Once());
}
[TestMethod]
public void MakeTwoCallsSameIdPasses()
{
//ids intentionally equal:
int idOne=1;
int idTwo=1;
DataTable dt=new DataTable();
dt.Columns.Add("Id");
dt.Rows.Add(idOne);
dt.Rows.Add(idTwo);
IProcessor mock = Mock.Create<IProcessor>();
Runner runner = new Runner(mock);
runner.Process(dt);
//all asserts pass:
Mock.Assert(()=>mock.Process(Arg.IsAny<MyArgs>()), Occurs.Exactly(2));
//The following two pass:
Mock.Assert(()=>mock.Process(Arg.Matches<MyArgs>
(d=>d.Id==idOne)),Occurs.Exactly(2));
Mock.Assert(()=>mock.Process(Arg.Matches<MyArgs>
(d=>d.Id==idTwo)),Occurs.Exactly(2));
}
}
public interface IProcessor
{
void Process(MyArgs args);
}
public class MyArgs
{
public void UpdateId(int newId)
{
this.Id = newId;
}
public int Id {get; private set;}
}
public class Runner
{
private IProcessor processor;
public Runner(IProcessor processor)
{
this.processor=processor;
}
public void Process(DataTable dt)
{
MyArgs args = new MyArgs();
foreach(DataRow row in dt.Rows)
{
int id = Convert.ToInt32(row["Id"]);
args.UpdateId(id);
processor.Process(args);
}
}
}
编辑:在失败的测试方法中,如果我完全删除其中一个 int 变量并明确断言 other 只被调用一次,则测试通过。只有当我将第二个不同的值加入组合时,事情似乎才会变得糟糕。
mock 记录它被同一个 MyArgs
实例调用了两次。但是,两次调用之间的实例内容不同这一事实已丢失。当您到达断言调用的位置时,记录的 MyArgs
参数在失败的测试中有一个 Id
等于 2。
如果您将 Process
方法更改为
public void Process(DataTable dt)
{
foreach(DataRow row in dt.Rows)
{
MyArgs args = new MyArgs();
int id = Convert.ToInt32(row["Id"]);
args.UpdateId(id);
processor.Process(args);
}
}
两个测试都会通过。
编辑:
您可以通过在安排中陈述您的期望来使用 JustMock 获得所需的行为,如下所示:
[TestMethod]
public void MakeTwoCallsDifferentIdsFails()
{
int idOne = 1;
int idTwo = 2;
DataTable dt = new DataTable();
dt.Columns.Add("Id");
dt.Rows.Add(idOne);
dt.Rows.Add(idTwo);
IProcessor mock = Mock.Create<IProcessor>();
Mock.Arrange(() => mock.Process(Arg.Matches<MyArgs>(d => d.Id == idOne))).OccursOnce();
Mock.Arrange(() => mock.Process(Arg.Matches<MyArgs>(d => d.Id == idTwo))).OccursOnce();
Runner runner = new Runner(mock);
runner.Process(dt);
Mock.Assert(mock);
}
之所以可行,是因为一旦发生正确的调用,就会将发生期望标记为满足,因此 Arg.Matches
的谓词使用传递给方法的参数的实时值,而不是post 保存的参数实例可能发生变异的事实。
因为我无法让我们选择的模拟框架 (JustMock) 完成我想要的,所以我最终应用了 KISS,忘记了这个测试的模拟框架,并使用了类似以下:
我将测试方法更改为如下所示:
[TestMethod]
public void TwoCallsDifferentIds()
{
int idOne = 1;
int idTwo = 2;
DataTable dt = new DataTable();
dt.Columns.Add("Id");
dt.Rows.Add(idOne);
dt.Rows.Add(idTwo);
FakeProcessor processor = new FakeProcessor();
Runner runner = new Runner(processor);
runner.Process(dt);
Assert.AreEqual(2, processor.MyArgsIds.Count);
Assert.AreEqual(1, processor.MyArgsIds[0]);
Assert.AreEqual(2, processor.MyArgsIds[1]);
}
并添加了以下假的:
public class FakeProcessor : IProcessor
{
private IList<int> mList = new List<int>();
public IList<int> MyArgsIds
{
get { return mList; }
}
public void Process(MyArgs args)
{
mList.Add(args.Id);
}
}
也许没有那么圆滑,但它让我得到了我想要的测试。
有人可以看看下面的演示代码,让我知道我看到的是我的错误还是 Telerik 问题?
我使用的是 Telerik.JustMock v. 2014.1.1519.1。和 Microsoft.VisualStudio.QualityTools.UnitTestFramework 诉 10.0.0.0。
正如代码注释所指出的,当 id 变量相等时(对每个 id 调用一次),我得到了预期的结果,但当它们不同时却没有。当我逐步完成第一个测试时,我可以看到正在进行的预期调用,但 JustMock 然后告诉我它们没有进行。
我将不胜感激任何建设性的想法。希望这不是我睡眠不足的问题...
[TestClass]
public class RunnerTests
{
[TestMethod]
public void MakeTwoCallsDifferentIdsFails()
{
int idOne=1;
int idTwo=2;
DataTable dt=new DataTable();
dt.Columns.Add("Id");
dt.Rows.Add(idOne);
dt.Rows.Add(idTwo);
IProcessor mock = Mock.Create<IProcessor>();
Runner runner = new Runner(mock);
runner.Process(dt);
Mock.Assert(()=>mock.Process(Arg.IsAny<MyArgs>()), Occurs.Exactly(2));
//The following two asserts fail (with 0 calls made to mock), regardless of sequence:
Mock.Assert(()=>mock.Process(Arg.Matches<MyArgs>
(d=>d.Id==idOne)),Occurs.Once());
Mock.Assert(()=>mock.Process(Arg.Matches<MyArgs>
(d=>d.Id==idTwo)),Occurs.Once());
}
[TestMethod]
public void MakeTwoCallsSameIdPasses()
{
//ids intentionally equal:
int idOne=1;
int idTwo=1;
DataTable dt=new DataTable();
dt.Columns.Add("Id");
dt.Rows.Add(idOne);
dt.Rows.Add(idTwo);
IProcessor mock = Mock.Create<IProcessor>();
Runner runner = new Runner(mock);
runner.Process(dt);
//all asserts pass:
Mock.Assert(()=>mock.Process(Arg.IsAny<MyArgs>()), Occurs.Exactly(2));
//The following two pass:
Mock.Assert(()=>mock.Process(Arg.Matches<MyArgs>
(d=>d.Id==idOne)),Occurs.Exactly(2));
Mock.Assert(()=>mock.Process(Arg.Matches<MyArgs>
(d=>d.Id==idTwo)),Occurs.Exactly(2));
}
}
public interface IProcessor
{
void Process(MyArgs args);
}
public class MyArgs
{
public void UpdateId(int newId)
{
this.Id = newId;
}
public int Id {get; private set;}
}
public class Runner
{
private IProcessor processor;
public Runner(IProcessor processor)
{
this.processor=processor;
}
public void Process(DataTable dt)
{
MyArgs args = new MyArgs();
foreach(DataRow row in dt.Rows)
{
int id = Convert.ToInt32(row["Id"]);
args.UpdateId(id);
processor.Process(args);
}
}
}
编辑:在失败的测试方法中,如果我完全删除其中一个 int 变量并明确断言 other 只被调用一次,则测试通过。只有当我将第二个不同的值加入组合时,事情似乎才会变得糟糕。
mock 记录它被同一个 MyArgs
实例调用了两次。但是,两次调用之间的实例内容不同这一事实已丢失。当您到达断言调用的位置时,记录的 MyArgs
参数在失败的测试中有一个 Id
等于 2。
如果您将 Process
方法更改为
public void Process(DataTable dt)
{
foreach(DataRow row in dt.Rows)
{
MyArgs args = new MyArgs();
int id = Convert.ToInt32(row["Id"]);
args.UpdateId(id);
processor.Process(args);
}
}
两个测试都会通过。
编辑:
您可以通过在安排中陈述您的期望来使用 JustMock 获得所需的行为,如下所示:
[TestMethod]
public void MakeTwoCallsDifferentIdsFails()
{
int idOne = 1;
int idTwo = 2;
DataTable dt = new DataTable();
dt.Columns.Add("Id");
dt.Rows.Add(idOne);
dt.Rows.Add(idTwo);
IProcessor mock = Mock.Create<IProcessor>();
Mock.Arrange(() => mock.Process(Arg.Matches<MyArgs>(d => d.Id == idOne))).OccursOnce();
Mock.Arrange(() => mock.Process(Arg.Matches<MyArgs>(d => d.Id == idTwo))).OccursOnce();
Runner runner = new Runner(mock);
runner.Process(dt);
Mock.Assert(mock);
}
之所以可行,是因为一旦发生正确的调用,就会将发生期望标记为满足,因此 Arg.Matches
的谓词使用传递给方法的参数的实时值,而不是post 保存的参数实例可能发生变异的事实。
因为我无法让我们选择的模拟框架 (JustMock) 完成我想要的,所以我最终应用了 KISS,忘记了这个测试的模拟框架,并使用了类似以下:
我将测试方法更改为如下所示:
[TestMethod]
public void TwoCallsDifferentIds()
{
int idOne = 1;
int idTwo = 2;
DataTable dt = new DataTable();
dt.Columns.Add("Id");
dt.Rows.Add(idOne);
dt.Rows.Add(idTwo);
FakeProcessor processor = new FakeProcessor();
Runner runner = new Runner(processor);
runner.Process(dt);
Assert.AreEqual(2, processor.MyArgsIds.Count);
Assert.AreEqual(1, processor.MyArgsIds[0]);
Assert.AreEqual(2, processor.MyArgsIds[1]);
}
并添加了以下假的:
public class FakeProcessor : IProcessor
{
private IList<int> mList = new List<int>();
public IList<int> MyArgsIds
{
get { return mList; }
}
public void Process(MyArgs args)
{
mList.Add(args.Id);
}
}
也许没有那么圆滑,但它让我得到了我想要的测试。