我正在进行一些单元测试,并使用Moq模拟一些属性.
现在,这是一个Controller测试(ASP.NET MVC 3).我的控制器派生自一个名为AbstractController的抽象控制器.
该控制器依赖于Http Context(为了做主题,基于HTTP HOST头的特定于域的逻辑等).
这是通过名为WebSiteSettings的属性完成的:
public abstract class AbstractController : Controller
{
public WebSiteSettings WebSiteSettings { get; private set; }
// other code
}
Run Code Online (Sandbox Code Playgroud)
注意私人集 - ctor设置它.所以,我把它改成使用了一个界面,这就是我所嘲笑的:
public IWebSiteSettings WebSiteSettings { get; private set; }
Run Code Online (Sandbox Code Playgroud)
然后我创建了一个"FakeWebSiteSettings",它嘲笑Http Context以便它读取HTTP头.
问题是,当我运行测试时,我得到一个NotSupportedException:
非虚拟(在VB中可覆盖)成员的设置无效:x => x.WebSiteSettings
这是相关的模拟代码:
var mockWebSiteSettings = new Mock<FakeWebSiteSettings>();
var mockController = new Mock<MyController>(SomeRepository);
mockController.Setup(x => x.WebSiteSettings).Returns(mockWebSiteSettings.Object);
_controller = mockController.Object;
var httpContextBase = MvcMockHelpers.FakeHttpContext();
httpContextBase.Setup(x => …Run Code Online (Sandbox Code Playgroud) 给定一个IService具有Method1()和的接口Method2().
我想测试当Method1()抛出一个Exception,Method2()被调用并返回一个给定的值.
(投掷Method2()时被称为Method1()).
因此我需要Method2()用假的测试真实Method1(),它们是相同界面的方法.
这是我的测试代码:
MBase sut.MethodX()是唯一的切入点.它使用IService.
我的目标是断言Method2()返回一些东西.
// Arrange
// Fake bytes in.
var networkStreamMock = new Mock<INetworkStream>();
networkStreamMock.Method1(x => x.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>())).Returns(It.IsAny<byte[]>());
// Force throw TimeoutException.
var mock = new Mock<IService>();
mock.Setup(x => x.Method1(new Message
{
Xml = Xml,
}
)).Throws<TimeoutException>();
// Check Method 2 is called. (this is …Run Code Online (Sandbox Code Playgroud) 考虑.NET程序集中的方法:
public static string GetSecurityContextUserName()
{
//extract the username from request
string sUser = HttpContext.Current.User.Identity.Name;
//everything after the domain
sUser = sUser.Substring(sUser.IndexOf("\\") + 1).ToLower();
return sUser;
}
Run Code Online (Sandbox Code Playgroud)
我想使用Moq框架从单元测试中调用此方法.该程序集是webforms解决方案的一部分.单元测试看起来像这样,但我错过了Moq代码.
//arrange
string ADAccount = "BUGSBUNNY";
string fullADName = "LOONEYTUNES\BUGSBUNNY";
//act
//need to mock up the HttpContext here somehow -- using Moq.
string foundUserName = MyIdentityBL.GetSecurityContextUserName();
//assert
Assert.AreEqual(foundUserName, ADAccount, true, "Should have been the same User Identity.");
Run Code Online (Sandbox Code Playgroud)
问题:
MyIdentityBL.GetSecurityContextUserName()?我有一个api服务,调用另一个API服务.当我设置Mock对象时,它失败并出现错误:
NotSupportedException:expression引用不属于模拟对象的方法.
这是代码:
private Mock<IEnumerable<ICarrierApiService<AccountSearchModel>>> _mockCarrierService;
private Mock<IApiService<AccountSearchModel>> _mockApiService;
[SetUp]
public void SetUp()
{
_mockApiService = new Mock<IApiService<AccountSearchModel>>();
_mockCarrierService = new Mock<IEnumerable<ICarrierApiService<AccountSearchModel>>>();
_mockApiService.Setup(x => x.GetFromApiWithQuery(It.IsAny<string>())).Returns(ApiValue());
// Error occurred when call _mockApiService.GetFromApiWithQuery() in .Select()
_mockCarrierService.Setup(x => x
.Select(s => s
.GetFromApiWithQuery(It.IsAny<string>())).ToList())
.Returns(new List<IQueryable<AccountSearchModel>> { ApiValue() });
}
Run Code Online (Sandbox Code Playgroud)
我用Moq阅读表达式测试,但它对我的情况不起作用.如果我删除它_mockCarrierService.Setup(),测试用例可以运行但是失败,NullReferenceException因为它没有有效的List<IQueryable<AccountSearchModel>>设置.
知道我怎么能做到这一点?
脚注:当前的解决方案
FWIW,这是我目前使用的解决方案.我很乐意更好地解决这个问题(直到Moq开始支持模拟扩展方法).
private List<ICarrierApiService<AccountSearchModel>> _mockCarrierService;
private AccountSearchController _mockController;
private Mock<ICarrierApiService<AccountSearchModel>> _mockApiService;
[SetUp]
public void SetUp()
{
_mockApiService = …Run Code Online (Sandbox Code Playgroud) 我的C#解决方案中有3个项目.
签名具有公共和内部接口.它也有
[assembly: InternalsVisibleTo("Structures")]
[assembly: InternalsVisibleTo("Tests")]
Run Code Online (Sandbox Code Playgroud)
在AssemblyInfo.cs中.
结构有公共和内部类和
[assembly: InternalsVisibleTo("Tests")]
Run Code Online (Sandbox Code Playgroud)
在AssemblyInfo.cs中.
测试有下一个来源:
<packages>
<package id="Moq" version="4.2.1409.1722" targetFramework="net45" />
<package id="NUnit" version="2.6.4" targetFramework="net45" />
<package id="NUnitTestAdapter" version="1.2" targetFramework="net45" />
</packages>
Run Code Online (Sandbox Code Playgroud)
在packages.config中作为NuGet包.
我从Signatures和Structures的内部类中为内部接口编写了一些单元测试.运行,并有下一个结果:异常:
类型Signatures.InterfaceX对DynamicProxy不可见.无法为无法访问的类型创建代理.将类型设置为public或internal,并使用[assembly:InternalsVisibleTo(InternalsVisible.ToDynamicProxyGenAssembly2)]属性标记程序集.
似乎合乎逻辑.我将[assembly:InternalsVisibleTo("InternalsVisible.DynamicProxyGenAssembly2")]添加到了Signatures and Structures项目的装配信息中.运行,并有下一个结果:异常:
从程序集"DynamicProxyGenAssembly2,Version = 0.0.0.0,Culture = neutral,PublicKeyToken = null"中键入"Castle.Proxies.IReminiscenceableDataTableProxy"正在尝试实现无法访问的接口.
这是有帮助的,但没有.仅更改异常消息.
如何解决我的问题?
我非常感谢Moq的Loose模拟行为,在没有设定期望时返回默认值.这很方便,可以节省代码,也可以作为一种安全措施:在单元测试期间不会无意中调用依赖关系(只要它们是虚拟的).
但是,当被测方法恰好是虚拟时,我对如何保持这些好处感到困惑.
在这种情况下,我确实想要为这一个方法调用真正的代码,同时仍然对该类的其余部分进行松散的模拟.
我在搜索中找到的就是我可以设置mock.CallBase = true以确保调用该方法.但是,这会影响整个班级.我不想那样做,因为它让我陷入了隐藏调用依赖关系的类中所有其他属性和方法的两难境地:如果CallBase是真的那么我必须要么
我想我想要的是:
mock.Setup(m => m.VirtualMethod()).CallBase();
所以当我打电话时mock.Object.VirtualMethod(),Moq会调用真正的实现......
问:有了Moq,有什么方法可以测试一个虚方法,当我嘲笑这个类只存在几个依赖项时?即不使用CallBase = true并且必须存根所有依赖项?
用于说明的示例代码
(使用MSTest,InternalsVisibleTo DynamicProxyGenAssembly2)
在以下示例中,TestNonVirtualMethod传递但是TestVirtualMethod失败 - 返回null.
public class Foo
{
public string NonVirtualMethod() { return GetDependencyA(); }
public virtual string VirtualMethod() { return GetDependencyA();}
internal virtual string GetDependencyA() { return "! Hit REAL Dependency A !"; }
// [... Possibly many other dependencies ...]
internal virtual …Run Code Online (Sandbox Code Playgroud) 什么是最简洁的方式使用Moq来模拟一个方法,它会在第一次调用时抛出异常,然后在第二次调用它时成功?
我花了一个晚上试图模拟一个实现IQueryable的对象:
public interface IRepo<T> : IQueryable<T>
{
}
Run Code Online (Sandbox Code Playgroud)
我能想到的最好的是这样的:
var items = new Item[] {}.AsQueryable();
var repo = new Mock<IRepo>();
repo.Setup(r => r.GetEnumerator()).Returns(items.GetEnumerator());
repo.Setup(r => r.Provider).Returns(items.Provider);
repo.Setup(r => r.ElementType).Returns(items.ElementType);
repo.Setup(r => r.Expression).Returns(items.Expression);
Run Code Online (Sandbox Code Playgroud)
有没有更简洁的方法来做同样的事情?在IRepo中公开一个返回IQueryable的属性/方法会更容易,并且这样简单地模拟:
repo.Setup(r => r.GetItems()).Returns(new Items[]{ }.AsQueryable());
Run Code Online (Sandbox Code Playgroud)
但这不是我想做的事情=)
我是moq的新手,并设置了模拟,所以我可以做一些帮助.如何使用Moq模拟SqlDataReader?
更新
经过进一步测试,这是我到目前为止所做的:
private IDataReader MockIDataReader()
{
var moq = new Mock<IDataReader>();
moq.Setup( x => x.Read() ).Returns( true );
moq.Setup( x => x.Read() ).Returns( false );
moq.SetupGet<object>( x => x["Char"] ).Returns( 'C' );
return moq.Object;
}
private class TestData
{
public char ValidChar { get; set; }
}
private TestData GetTestData()
{
var testData = new TestData();
using ( var reader = MockIDataReader() )
{
while ( reader.Read() )
{
testData = new TestData
{
ValidChar = reader.GetChar( "Char" ).Value …Run Code Online (Sandbox Code Playgroud) 如果需要设置返回值,以及验证调用表达式的次数,可以在一个语句中执行此操作吗?
从我可以收集到的,Moq的Setup(SomeExpression).Verifiable()调用Verify(),基本上是一个Verify(SomeExpression, Times.AtLeastOnce)?即它验证表达式只被调用.
这是一个更好地解释问题的例子.对于接口:
interface IFoo
{
int ReturnSomething();
}
Run Code Online (Sandbox Code Playgroud)
以下两个块是否等效(除了第一个将验证标记为可验证的所有设置)?
void Test()
{
var mock = new Mock<IFoo>();
mock.Setup((m) => m.ReturnSomething()).Returns(1).Verifiable();
mock.Verify();
}
Run Code Online (Sandbox Code Playgroud)
和
void Test()
{
var mock = new Mock<IFoo>();
mock.Setup((m) => m.ReturnSomething()).Returns(1);
mock.Verify((m) => m.ReturnSomething(), Times.AtLeastOnce());
}
Run Code Online (Sandbox Code Playgroud)
如果我想验证调用次数(比如两次),这是唯一的方法,其中表达式重复进行安装和验证吗?
void Test()
{
var mock = new Mock<IFoo>();
mock.Setup((m) => m.ReturnSomething()).Returns(1);
mock.Verify((m) => m.ReturnSomething(), Times.Exactly(2));
}
Run Code Online (Sandbox Code Playgroud)
我只是不喜欢打电话给安装和验证.好吧,因为这对AAA来说是一个好主意,换句话说,我不喜欢重复设置和验证的表达式.目前我将表达式存储在变量中并将其传递给每个方法,但感觉不是那么干净.
PS - 上下文用于检查何时更新缓存(explirations等)的测试
moq ×10
unit-testing ×9
c# ×8
mocking ×5
nunit ×3
.net ×1
asp.net-mvc ×1
controller ×1
expression ×1
installation ×1
internal ×1
linq ×1
testing ×1
verify ×1