我有一个名为 A 的项目,其项目名为 ClassA 。 ClassA 有一个名为 ReadBlock()的方法,它会创建一个 CloudBlockBlob 对象并调用其中一个方法。
CloudBlockBlob 是一个位于 Microsoft.WindowsAzure.Storage.Blob 命名空间的类,位于 Microsoft.WindowsAzure.Storage.dll 中
我的项目 A 有一个名为 A.Tests 的单元测试项目。 现在,我想测试方法 ReadBlock()。为了测试它,我需要模拟 CloudBlockBlob 对象并拦截对其方法的调用,返回自定义值并验证方法是否被调用。
更新 问题是我是否可以在不修改项目 A 的代码的情况下执行此操作。
谢谢!
答案 0 :(得分:5)
如果不修改类A
代码,您将无法使用Moq来使用ReadBlock
方法。您将能够使用代码编织工具(MsFakes,Typemock隔离器等等)来使用此方法。
例如(MsFakes):
[TestMethod]
public void TestMethod1()
{
using (ShimsContext.Create())
{
ShimCloudBlockBlob.AllInstances.<the method you want to override> = (<the method arguments>) => {};
}
}
在using
范围内,您可以通过属性AllInstances
覆盖CloudBlockBlob拥有的任何方法。
在下一节中,我将讨论您拥有的所有其他选项......
选项1:
public class A
{
private IBlockBlob _blockBlob;
public A(IBlockBlob blockBlob)
{
_blockBlob = blockBlob;
}
public void ReadBlock()
{
_blockBlob.DoSomething();
}
}
由于每次调用ReadBlock
(您的方法的当前行为)时都会创建一个新实例,因此最好注入工厂而不是包装器,而DoSomething
应该是create
;选项2:
public class A
{
private readonly IFactoryBlockBlob _blobFctory;
public A(IFactoryBlockBlob blobFctory)
{
_blobFctory = blobFctory;
}
public void ReadBlock()
{
var blob = _blobFctory.Create();
}
}
但是,根据您的问题和您的评论,您的班级似乎“有依赖”,而不是“需要依赖”。
(马克西门子写了一本关于DI的好书,这张图取自his book)
使用这条新信息,您的方法应该是这样的;选项3:
public class A
{
public void ReadBlock(ICloudBlob blob)
{
}
}
但您不想更改方法的签名:
public class A
{
public void ReadBlock()
{
ReadBlock(new CloudBlockBlob(<the params bla bla...>));
}
internal void ReadBlock(ICloudBlob blob)
{
}
}
添加InternalsVisibleToAttribute
,然后验证内部方法的行为。
通过阅读这些内容,我觉得您的课程是一种“遗留代码”,意味着它可以完成工作,不会改变,并且验证其行为可能是浪费时间。在过去,我发布了一个图表(in this answer),可以帮助您决定处理此案例的方法。
答案 1 :(得分:3)
最好为CloudBlockBlob创建一个非常简单的可模拟包装器,以提高代码的可测试性并使用依赖性反转注入它。
现在你可能有类似的东西:
public class A
{
public void ReadBlock()
{
var blockBlob = new CloudBlockBlob();
blockBlob.DoSomething();
}
}
相反,将您的包装器注入A中,以便A不知道对CloudBlockBlob的依赖:
public class A
{
IBlockBlob _blockBlob
public A(IBlockBlob blockBlob)
{
_blockBlob = blockBlob;
}
public void ReadBlock()
{
_blockBlob.DoSomething();
}
}
答案 2 :(得分:1)
免责声明,我在Typemock工作。
您可以在不使用Isolator修改项目A的代码的情况下完成此操作。 有一个简单的例子可以做到:
public class Foo
{
public void ReadBlock()
{
var block = new CloudBlockBlob(new Uri("http://myUrl/%2E%2E/%2E%2E"));
var name = block.Name;
}
}
[TestMethod, Isolated]
public void TestReadBlock()
{
//Arrange
var fakeBlock = Isolate.Fake.AllInstances<CloudBlockBlob>();
Isolate.WhenCalled(() => fakeBlock.Name).WillReturn("Name");
//Act
var foo = new Foo();
foo.ReadBlock();
//Assert
Isolate.Verify.WasCalledWithAnyArguments(() => fakeBlock.Name);
}
希望它有所帮助!