用JustMock测试你的应用程序
本主题将指导您通过几个简单的步骤来使用Telerik®JustMock轻松测试您的应用程序。您将理解一个简单的原理,称为Arrange / Act / Assert,并熟悉框架中的核心方法和属性,这些方法和属性在最常见的测试场景中使用
为了说明下一个例子中JustMock的用法,我们将使用一个样本仓库(warehouse)和一个依赖订单对象(Order)。仓库持有不同产品的库存。订单包含产品和数量。
仓库界面和订单类看起来像这样:
publicdelegatevoidProductRemoveEventHandler(string productName,int quantity);
publicinterfaceIwarehouse
{
eventProductRemoveEventHandlerProductRemoved;
stringManager{get;set;}
boolHasInventory(string productName,int quantity);
voidRemove(string productName,int quantity);
}
publicclassOrder
{
publicOrder(string productName,int quantity)
{
this.ProductName= productName;
this.Quantity= quantity;
}
publicstringProductName{get;privateset;}
publicintQuantity{get;privateset;}
publicboolIsFilled{get;privateset;}
publicvoidFill(Iwarehouse warehouse)
{
if(warehouse.HasInventory(this.ProductName,this.Quantity))
{
warehouse.Remove(this.ProductName,this.Quantity);
}
}
publicvirtualstringReceipt(DateTime orderDate)
{
returnstring.Format("Ordered {0} {1} on{2}",this.Quantity,this.ProductName, orderDate.ToString("d"));
}
}
方法
DoInstead
DoInstead
当您想要通过用自定义操作替换方法来更改方法的行为时,可以使用该方法。我们用上面的例子来说明如何使用DoInstead
。
[TestMethod]
publicvoidDoInstead_TestMethod()
{
//Arrange
var warehouse =Mock.Create<Iwarehouse>();
var order =newOrder("Camera",2);
bool called =false;
Mock.Arrange(()=> warehouse.HasInventory("Camera",2)).DoInstead(()=> called =true);
//Act
order.Fill(warehouse);
//Assert
Assert.IsTrue(called);
}
简单的说 - 我们安排,当仓库的HasInventory
方法调用参数“camera”和2,我们将执行行动“ ()=>called=true ”,而不是调用实际的方法。
CallOriginal
在某些情况下,您可能希望在调用原始方法实现时使用特定的值调用该方法,并使用其他值调用模拟。为此,您可以使用该CallOriginal
方法。
[TestMethod]
publicvoidCallOriginal_TestMethod()
{
//Arrange
var order =Mock.Create<Order>(Behavior.CallOriginal,"Camera",2);
Mock.Arrange(()=> order.Receipt(DateTime.Today)).CallOriginal();
Mock.Arrange(()=> order.Receipt(Arg.Matches<DateTime>(d => d >DateTime.Today))).Returns("InvalidDateTime");
//Act
var callWithToday =order.Receipt(DateTime.Today);
var callWithDifferentDay = order.Receipt(DateTime.Today.AddDays(1));
//Assert
Assert.AreEqual("Ordered 2 Camera on "+DateTime.Today.ToString("d"), callWithToday);
Assert.AreEqual("Invalid DateTime", callWithDifferentDay);
}
在这个例子中,我们安排当order.Receipt
用参数调用DateTime.Today
方法时,应该调用原来的方法实现。但是,一旦晚于日期调用相同的方法,DateTime.Today
我们将返回“Invalid DateTime”。
throws
在Throws
当你想抛出一个异常特定方法调用方法时使用。在下面的例子中,我们抛出一个无效的操作异常,试图调用仓库。删除零个数量。
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
publicvoidThrows_TestMethod()
{
//Arrange
var order =newOrder("Camera",0);
var warehouse =Mock.Create<Iwarehouse>();
//Set up that the ware house has inventory of any products with any quantities.
Mock.Arrange(()=> warehouse.HasInventory(Arg.IsAny<string>(),Arg.IsAny<int>())).Returns(true);
//Set up that call to warehouse.Remove with zero quantity is invalid and throwsan exception.
Mock.Arrange(()=> warehouse.Remove(Arg.IsAny<string>(),Arg.Matches<int>(x => x ==0)))
.Throws(newInvalidOperationException());
//Act
order.Fill(warehouse);
}
在这种情况下,我们使用ExpectedException
属性Microsoft.VisualStudio.TestTools.UnitTesting
来验证类型InvalidOperationException
的异常是否被抛出。
Machers
匹配器让你忽略传递实际值作为模拟中使用的参数。相反,它们给你传递一个满足参数类型或期望值范围的表达式的可能性。例如,如果方法接受字符串作为第一个参数,则不需要传递特定的字符串,如“Camera”,而是可以使用Arg.IsAny<string>()
。
JustMock支持三种类型的匹配器:
1. Arg.IsAny<[Type]>();
2. Arg.IsInRange([FromValue : int], [ToValue : int],[RangeKind])
3. Arg.Matches(Expression> expression)
我们来看看它们的详细用法。
Arg.IsAny();
我们已经在上面的一个例子中使用了这个匹配器。
Mock.Arrange(()=> warehouse.HasInventory(Arg.IsAny<string>(),Arg.IsAny<int>())).Returns(true);
这个匹配器指定当HasInventory
任何字符串作为第一个参数调用方法,任何int作为第二个参数时,它应该返回true
。
Arg.IsInRange(int from,int to,RangeKind range)
IsInRange匹配器让我们安排一个预期值范围的调用。通过RangeKind
论证,我们可以指定给定的范围是包含还是排除其边界。
对于范围从0到5的参数值,将返回以下内容true
:
Mock.Arrange(()=> foo.Echo(Arg.IsInRange(0,5,RangeKind.Inclusive))).Returns(true);
Arg.Matches (Expression> expression)
这是最灵活的匹配器,它允许你指定你自己的匹配表达式。我们用一个简单的例子来说明:
Mock.Arrange(()=> foo.Echo(Arg.Matches<int>( x => x <10)).Returns(true);
属性
在上面的例子中,我们只模拟方法,但是你也可以用同样的方法来模拟属性。
[TestMethod]
publicvoidMockingProperties_TestMethod()
{
//Arrange
var warehouse =Mock.Create<Iwarehouse>();
Mock.Arrange(()=> warehouse.Manager).Returns("John");
string manager =string.Empty;
//Act
manager = warehouse.Manager;
//Assert
Assert.AreEqual("John", manager);
}
另外,还可以给属性赋值
[TestMethod]
[ExpectedException(typeof(StrictMockException))]
publicvoidMockingProperties_PropertySet_TestMethod()
{
//Arrange
var warehouse =Mock.Create<Iwarehouse>(Behavior.Strict);
Mock.ArrangeSet(()=> warehouse.Manager="John");
//Act
warehouse.Manager="Scott";
}
在安排步骤中,我们设置仓库经理只能设置为“John”。但是在行动步骤中,我们将经理设置为“Scott”。这抛出了一个模拟异常。请记住,这只会在您使用StrictBehavior创建模拟时才起作用。
另一个常用的技巧是断言将属性设置为特定值会引发异常。我们来安排一下
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
publicvoidMockingProperties_PropertySet_Throws_TestMethod()
{
//Arrange
var warehouse =Mock.Create<Iwarehouse>();
Mock.ArrangeSet(()=> warehouse.Manager="John").Throws<ArgumentException>();
//Act
//that's ok
warehouse.Manager="Scott";
//but that would throw an ArgumentException
warehouse.Manager="John";
}
在这里,我们使用Throws
上面讨论的方法来表明如果warehouse.Manager
设置为“John”,则应抛出异常。
活动
该方法Raises
允许您在调用方法时引发事件并传递特定的事件参数。回到我们的仓库示例,我们可能想要在调用ProductRemoved
该Remove
方法时引发事件。
[TestMethod]
publicvoidRaisingAnEvent_TestMethod()
{
//Arrange
var warehouse =Mock.Create<Iwarehouse>();
Mock.Arrange(()=> warehouse.Remove(Arg.IsAny<string>(),Arg.IsInRange(int.MinValue,int.MaxValue,RangeKind.Exclusive)))
.Raises(()=> warehouse.ProductRemoved+=null,"Camera",2);
string productName =string.Empty;
int quantity =0;
warehouse.ProductRemoved+=(p, q)=>{ productName = p; quantity =q;};
//Act
warehouse.Remove(Arg.AnyString,Arg.AnyInt);
//Assert
Assert.AreEqual("Camera", productName);
Assert.AreEqual(2, quantity);
}
在安排步骤中,我们设置一旦仓库的Remove
方法被调用,我们将ProductRemoved
用参数“Camera”和2 来提升调用事件。
项目GitHub地址:https://github.com/liuzhenyulive/JustMockDemo
参考文献:http://docs.telerik.com/devtools/justmock/getting-started/quick-start#testing-your-application-with-justmock