先模拟一个方法而不先嘲笑一个类
我正在使用moq4来模拟我的UnitTests中的东西。 我有一个类说TestClass ,其中一个名为TestMethod的方法是我想要测试的方法。 所以我的问题是我的TestMethod需要检查我的TestClass中的testlist 。 像这样:-
public Class TestClass { public readonly ISomeService _someService; public bool TestProperty { get; private set; } public List testlist { get; private set; } Public TestClass(ISomeService someService) { _someService = someService; } public async Task TestMethod(string sd) { if(TestProperty) // here how can i control this property in UnitTest. return false; testlist.contains(sd); // check in this list but list will be null. } public async Task<List> SetTestListAndProperty() { testlist.Add("2"); testlist.Add("3"); TestProperty = false; } }
现在在testmethodtests中,我已经模拟了ISomeService并传递给了TestClass构造函数 。
var someservicemock = new Mock(); var testclassobj = new TestClass(someservicemock.Object);
现在我打电话给我的TestMethod
result = await testclassobj.TestMethod("id"); // it is throwing the exception that testlist is null.
//当TestProperty为false时,我也想测试TestMethod。 那么如何设置此属性它没有公共setter。 它由其他methd( SetTestListAndProperty )设置。
所以我的问题是我可以在不模仿TestClass的情况下模拟TestClass 测试列表吗? 如果这种方式有误,请告诉我,或者您也知道任何解决方法。 干杯
我认为你的问题归结为:当被测方法依赖于由其他方法设置的内部状态时该怎么办?
这是一个例子:
class MyCollection : IEnumerable { private List _list = new List (); private Receiver _receiver; public MyCollection() { _receiver = receiver; } public void Add(T item) { _list.Add(item); } public void RemoveAndNotify(T item) { _list.Remove(item); _receiver.Notify(item); } public IEnumerator GetEnumerator() { return _list.GetEnumerator(); } }
(顺便说一句,调用你的类型TestClass
, TestProperty
和testlist
并没有真正的帮助 – 读者不知道这些东西是如何相互关联的。它比令人困惑的Foo,Bar和Baz更令人困惑。使用猫,狗,人等等,具体而有意义的名称。)
- 所以,假设您想要对
RemoveAndNotify
方法进行unit testing。 但是你不能先删除一个项目而不先添加一个项目! 这是否意味着我必须模拟内部_list
来插入虚拟数据? 没有! - 另外,如何validation项目是否确实已从内部列表中删除? 我是否必须以某种方式检索内部_list并检查其内容(通过注入,reflection等)? 没有!
我认为你的问题的关键是:unit testing不一定测试一种方法 ! unit testing测试尽可能小的行为单位。
该行为单元可能涉及调用一种或多种方法。 所以这是我如何unit testing与删除项目相关的行为。 我们想要测试两种行为 。
- 我们想要在我们删除它之后测试该集合不再有项目。
- 我们想测试一个项目被删除时接收者会收到通知。
[Fact] public void RemoveAndNotify_RemovesItem() { //Arrange var mockReceiver = ... var collection = new MyCollection(mockReceiver.Object); collection.Add(5); //Act collection.Remove(5); //Assert //internally calls GetEnumerator to verify that the collection no longer contains "5" Assert.AreEqual(Enumerable.Empty (), collection); } [Fact] public void RemoveAndNotify_NotifiesReceiver() { //Arrange var mockReceiver = ... var collection = new MyCollection (mockReceiver.Object); collection.Add(5); //Act collection.Remove(5); //Assert mockReceiver.Verify(rec => rec.Notify(5), Times.Once()); }
如您所见,第一个unit testing在类( Add
, Remove
和GetEnumerator
)上调用了三个方法,以便测试一个行为单元 。
底线:忘记“测试中的方法”,并考虑“被测试的行为”。 你正在测试行为,而不是方法。
两件事情:
-
我不建议你嘲笑一切 。 在收集的情况下,使用真实(非模拟)对象是完全可以的。 模拟列表时的好处在哪里? 通过检查
testlist
属性,您可以非常轻松地测试所有内容。 -
(这是附注,但也解决了您的第一条评论。)尝试使您的类型不可变。 您可以找到有关此主题的大量文章。 只是搜索它。 好处是您不必处理
NullReferenceException
和其他一些场景。 对于您的示例,这意味着您应该在构造函数中初始化testlist
。
我将创建另一个构造函数,将列表作为参数,以便您可以在测试中创建并填充包含测试数据的列表,然后将其提供给测试中的类。
public TestClass(ISomeService service, IList list) { _someService = service; testList = list; }
使用此构造函数,testList不会为null,除非您传入null。
我知道有些人不喜欢添加仅用于测试的方法。 如果您不想公开另一个仅用于测试的构造函数,则可以将新构造函数标记为internal
并仅将内部结构暴露给测试程序集。
internal TestClass(ISomeService service, IList list) { ... }
在项目的AssemblyInfo.cs
:
[assembly: InternalsVisibleTo("your test assembly");
编辑:对于属性。 如果您具有不公开setter的属性,则可以将属性标记为virtual
并使用Moq的SetupGet
方法设置属性返回值。
public class ClassUnderTest { public virtual string TestProperty { get; private set; } }
在你的测试中:
上述就是C#学习教程:先模拟一个方法而不先嘲笑一个类分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
public void SomeTest() { var mock = new Mock(); mock.SetupGet(m => m.TestProperty).Returns("hi"); var result = mock.Object.TestProperty; Assert.That(result, Is.EqualTo("hi"); // should pass }
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1004818.html