c# – 单元测试,确保良好的覆盖范围,同时避免不必要的测试
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了c# – 单元测试,确保良好的覆盖范围,同时避免不必要的测试,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含6823字,纯文字阅读大概需要10分钟。
内容图文
![c# – 单元测试,确保良好的覆盖范围,同时避免不必要的测试](/upload/InfoBanner/zyjiaocheng/799/9e64a9e492f648b6aa969a6034e74b05.jpg)
我编写了一个类,它是一个可枚举的包装器,用于缓存底层可枚举的结果,只有在枚举并到达缓存结果的末尾时才获取下一个元素.它可以是多线程的(获取另一个线程中的下一个项目)或单线程(获取当前线程中的下一个项目).
我正在阅读unit-testing,并希望得到适当的测试.我正在使用nunit.我的主要问题是我已经写了我的课并且正在使用它.它适用于我正在使用它(目前有一件事).所以,我正在编写我的测试,只是试着想一想可能出错的事情,因为我已经非正式地测试过,我可能会无意识地编写测试,我知道我已经检查过了.如何在太多/细粒度测试和太少测试之间获得写入平衡?
>我应该只测试公共方法/构造函数还是应该测试每种方法?
>我应该单独测试CachedStreamingEnumerable.CachedStreamingEnumerator类吗?
>目前我只是在将类设置为单线程时进行测试.考虑到在检索项目并添加到缓存之前我可能需要等待一段时间,我如何在多线程时测试它?
>为了确保良好的覆盖范围,我缺少哪些测试?我已经不需要了吗?
类的代码和下面的测试类.
CachedStreamingEnumerable
/// <summary>
/// An enumerable that wraps another enumerable where getting the next item is a costly operation.
/// It keeps a cache of items, getting the next item from the underlying enumerable only if we iterate to the end of the cache.
/// </summary>
/// <typeparam name="T">The type that we're enumerating over.</typeparam>
public class CachedStreamingEnumerable<T> : IEnumerable<T>
{
/// <summary>
/// An enumerator that wraps another enumerator,
/// keeping track of whether we got to the end before disposing.
/// </summary>
public class CachedStreamingEnumerator : IEnumerator<T>
{
public class DisposedEventArgs : EventArgs
{
public bool CompletedEnumeration;
public DisposedEventArgs(bool completedEnumeration)
{
CompletedEnumeration = completedEnumeration;
}
}
private IEnumerator<T> _UnderlyingEnumerator;
private bool _FinishedEnumerating = false;
// An event for when this enumerator is disposed.
public event EventHandler<DisposedEventArgs> Disposed;
public CachedStreamingEnumerator(IEnumerator<T> UnderlyingEnumerator)
{
_UnderlyingEnumerator = UnderlyingEnumerator;
}
public T Current
{
get { return _UnderlyingEnumerator.Current; }
}
public void Dispose()
{
_UnderlyingEnumerator.Dispose();
if (Disposed != null)
Disposed(this, new DisposedEventArgs(_FinishedEnumerating));
}
object System.Collections.IEnumerator.Current
{
get { return _UnderlyingEnumerator.Current; }
}
public bool MoveNext()
{
bool MoveNextResult = _UnderlyingEnumerator.MoveNext();
if (!MoveNextResult)
{
_FinishedEnumerating = true;
}
return MoveNextResult;
}
public void Reset()
{
_FinishedEnumerating = false;
_UnderlyingEnumerator.Reset();
}
}
private bool _MultiThreaded = false;
// The slow enumerator.
private IEnumerator<T> _SourceEnumerator;
// Whether we're currently already getting the next item.
private bool _GettingNextItem = false;
// Whether we've got to the end of the source enumerator.
private bool _EndOfSourceEnumerator = false;
// The list of values we've got so far.
private List<T> _CachedValues = new List<T>();
// An object to lock against, to protect the cached value list.
private object _CachedValuesLock = new object();
// A reset event to indicate whether the cached list is safe, or whether we're currently enumerating over it.
private ManualResetEvent _CachedValuesSafe = new ManualResetEvent(true);
private int _EnumerationCount = 0;
/// <summary>
/// Creates a new instance of CachedStreamingEnumerable.
/// </summary>
/// <param name="Source">The enumerable to wrap.</param>
/// <param name="MultiThreaded">True to load items in another thread, otherwise false.</param>
public CachedStreamingEnumerable(IEnumerable<T> Source, bool MultiThreaded)
{
this._MultiThreaded = MultiThreaded;
if (Source == null)
{
throw new ArgumentNullException("Source");
}
_SourceEnumerator = Source.GetEnumerator();
}
/// <summary>
/// Handler for when the enumerator is disposed.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Enum_Disposed(object sender, CachedStreamingEnumerator.DisposedEventArgs e)
{
// The cached list is now safe (because we've finished enumerating).
lock (_CachedValuesLock)
{
// Reduce our count of (possible) nested enumerations
_EnumerationCount--;
// Pulse the monitor since this could be the last enumeration
Monitor.Pulse(_CachedValuesLock);
}
// If we've got to the end of the enumeration,
// and our underlying enumeration has more elements,
// and we're not getting the next item already
if (e.CompletedEnumeration && !_EndOfSourceEnumerator && !_GettingNextItem)
{
_GettingNextItem = true;
if (_MultiThreaded)
{
ThreadPool.QueueUserWorkItem((Arg) =>
{
AddNextItem();
});
}
else
AddNextItem();
}
}
/// <summary>
/// Adds the next item from the source enumerator to our list of cached values.
/// </summary>
private void AddNextItem()
{
if (_SourceEnumerator.MoveNext())
{
lock (_CachedValuesLock)
{
while (_EnumerationCount != 0)
{
Monitor.Wait(_CachedValuesLock);
}
_CachedValues.Add(_SourceEnumerator.Current);
}
}
else
{
_EndOfSourceEnumerator = true;
}
_GettingNextItem = false;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
lock (_CachedValuesLock)
{
var Enum = new CachedStreamingEnumerator(_CachedValues.GetEnumerator());
Enum.Disposed += new EventHandler<CachedStreamingEnumerator.DisposedEventArgs>(Enum_Disposed);
_EnumerationCount++;
return Enum;
}
}
}
CachedStreamingEnumerableTests
[TestFixture]
public class CachedStreamingEnumerableTests
{
public bool EnumerationsAreSame<T>(IEnumerable<T> first, IEnumerable<T> second)
{
if (first.Count() != second.Count())
return false;
return !first.Zip(second, (f, s) => !s.Equals(f)).Any(diff => diff);
}
[Test]
public void InstanciatingWithNullParameterThrowsException()
{
Assert.Throws<ArgumentNullException>(() => new CachedStreamingEnumerable<int>(null, false));
}
[Test]
public void SameSequenceAsUnderlyingEnumerationOnceCached()
{
var SourceEnumerable = Enumerable.Range(0, 10);
var CachedEnumerable = new CachedStreamingEnumerable<int>(SourceEnumerable, false);
// Enumerate the cached enumerable completely once for each item, so we ensure we cache all items
foreach (var x in SourceEnumerable)
{
foreach (var i in CachedEnumerable)
{
}
}
Assert.IsTrue(EnumerationsAreSame(Enumerable.Range(0, 10), CachedEnumerable));
}
[Test]
public void CanNestEnumerations()
{
var SourceEnumerable = Enumerable.Range(0, 10).Select(i => (decimal)i);
var CachedEnumerable = new CachedStreamingEnumerable<decimal>(SourceEnumerable, false);
Assert.DoesNotThrow(() =>
{
foreach (var d in CachedEnumerable)
{
foreach (var d2 in CachedEnumerable)
{
}
}
});
}
}
解决方法:
广告1)
如果你需要测试私有方法,这应该告诉你一些事情;可能你的班级有太多的责任.通常,私有方法是等待出生的单独类:-)
广告2)
是
广告3)
遵循与1相同的参数,如果可以避免,则可能不会在类内部完成线程功能.我记得罗伯特·马丁在“清洁代码”中读到了一些相关内容.他指出,线程是一个单独的问题,应该与业务逻辑的其他和平分开.
广告4)
私人方法是最难掩盖的.因此,我再次转向我的答案1.如果你的私人方法是单独的类中的公共方法,它们将更容易覆盖.此外,您的主要课程的测试将更容易理解.
问候,
莫滕
内容总结
以上是互联网集市为您收集整理的c# – 单元测试,确保良好的覆盖范围,同时避免不必要的测试全部内容,希望文章能够帮你解决c# – 单元测试,确保良好的覆盖范围,同时避免不必要的测试所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。