- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我有这样一个界面:
[ContractClass(typeof(ContractStockDataProvider))]
public interface IStockDataProvider
{
/// <summary>
/// Collect stock data from cache/ persistence layer/ api
/// </summary>
/// <param name="symbol"></param>
/// <returns></returns>
Task<Stock> GetStockAsync(string symbol);
/// <summary>
/// Reset the stock history values for the specified date
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
Task UpdateStockValuesAsync(DateTime date);
/// <summary>
/// Updates the stock prices with the latest values in the StockHistories table.
/// </summary>
/// <returns></returns>
Task UpdateStockPricesAsync();
/// <summary>
/// Determines the last population date from the StockHistories table, and
/// updates the table with everything available after that.
/// </summary>
/// <returns></returns>
Task BringStockHistoryCurrentAsync();
event Action<StockEventArgs> OnFeedComplete;
event Action<StockEventArgs> OnFeedError;
}
我有一个相应的合约类:
[ContractClassFor(typeof (IStockDataProvider))]
public abstract class ContractStockDataProvider : IStockDataProvider
{
public event Action<StockEventArgs> OnFeedComplete;
public event Action<StockEventArgs> OnFeedError;
public Task BringStockHistoryCurrentAsync()
{
return default(Task);
}
public Task<Stock> GetStockAsync(string symbol)
{
Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(symbol), "symbol required.");
Contract.Requires<ArgumentException>(symbol.Equals(symbol.ToUpperInvariant(), StringComparison.InvariantCulture),
"symbol must be in uppercase.");
return default(Task<Stock>);
}
public Task UpdateStockPricesAsync()
{
return default(Task);
}
public Task UpdateStockValuesAsync(DateTime date)
{
Contract.Requires<ArgumentOutOfRangeException>(date <= DateTime.Today, "date cannot be in the future.");
return default(Task);
}
}
我做了这样一个单元测试:
[TestClass]
public class StockDataProviderTests
{
private Mock<IStockDataProvider> _stockDataProvider;
[TestInitialize]
public void Initialize()
{
_stockDataProvider = new Mock<IStockDataProvider>();
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public async Task GetStockAsyncSymbolEmptyThrowsArgumentException()
{
//arrange
var provider = _stockDataProvider.Object;
//act
await provider.GetStockAsync(string.Empty);
//assert
Assert.Fail("Should have thrown ArgumentException");
}
}
根据我的阅读,这应该足以通过单元测试,但在执行时,单元测试因未抛出异常而失败。
我不是要测试合约功能,但我有兴趣测试验证逻辑以确保满足我对 IStockDataProvider 接口(interface)的具体实现的要求。
我做错了吗?我如何使用我的单元测试来验证我是否正确指定了我的输入?
更新
因此,虽然模拟接口(interface)和测试验证逻辑似乎不起作用,但我的具体类(不是从抽象类继承)在测试中正确地验证了输入。所以它可能只是在模拟中不受支持,尽管我不太清楚为什么。
最佳答案
您的模拟没有抛出异常的原因很简单。接口(interface)不能有方法。因此,您不能直接在接口(interface)上指定契约(Contract)。但是,你已经知道了这一点。这就是为什么您为接口(interface)创建了一个契约类(顺便说一下,它应该是一个私有(private)抽象类
)。
因为您正试图模拟界面,所以模拟工具对契约(Contract)一无所知。所有模拟工具所做的就是查看接口(interface)的定义并创建一个代理 对象。 proxy 是一个替身,一个替身,它根本没有任何行为!现在,借助像 Moq 这样的库,您可以使用 Returns(It.Is.Any())
等方法让这些代理具有行为。但是,此时这又将代理 变成了 stub 。此外,更重要的是,由于一个原因,这不适用于模拟库:代理 是在测试期间的运行时动态 创建的。因此,ccrewrite
不会“重写”代理。
那么您将如何测试您是否为契约(Contract)指定了正确的条件?
例如,您应该创建一个名为 MyProjectName.Tests.Stubs 的新库。然后,您应该为该项目中的界面创建一个实际的 stub 对象实例。它不必详细说明。足以让您调用单元测试中的方法来测试合约是否按预期工作。哦,还有一件更重要的事情要让它起作用:启用 执行运行时契约(Contract)检查 在这个新创建的用于调试构建的 stub 项目上。否则,您创建的继承自您的接口(interface)的 stub 将不会使用契约进行检测。
在您的单元测试项目中引用这个新的 MyProjectName.Tests.Stubs 程序集。使用 stub 来测试您的接口(interface)。这是一些代码(请注意,我使用的是您帖子中的代码——所以如果契约(Contract)没有按预期工作,请不要怪我——修复你的代码;)):
// Your Main Library Project
//////////////////////////////////////////////////////////////////////
[ContractClass(typeof(ContractStockDataProvider))]
public interface IStockDataProvider
{
/// <summary>
/// Collect stock data from cache/ persistence layer/ api
/// </summary>
/// <param name="symbol"></param>
/// <returns></returns>
Task<Stock> GetStockAsync(string symbol);
/// <summary>
/// Reset the stock history values for the specified date
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
Task UpdateStockValuesAsync(DateTime date);
/// <summary>
/// Updates the stock prices with the latest values in the StockHistories table.
/// </summary>
/// <returns></returns>
Task UpdateStockPricesAsync();
/// <summary>
/// Determines the last population date from the StockHistories table, and
/// updates the table with everything available after that.
/// </summary>
/// <returns></returns>
Task BringStockHistoryCurrentAsync();
event Action<StockEventArgs> OnFeedComplete;
event Action<StockEventArgs> OnFeedError;
}
// Contract classes should:
// 1. Be Private Abstract classes
// 2. Have method implementations that always
// 'throw new NotImplementedException()' after the contracts
//
[ContractClassFor(typeof (IStockDataProvider))]
private abstract class ContractStockDataProvider : IStockDataProvider
{
public event Action<StockEventArgs> OnFeedComplete;
public event Action<StockEventArgs> OnFeedError;
public Task BringStockHistoryCurrentAsync()
{
// If this method doesn't mutate state in the class,
// consider marking it with the [Pure] attribute.
//return default(Task);
throw new NotImplementedException();
}
public Task<Stock> GetStockAsync(string symbol)
{
Contract.Requires<ArgumentException>(
!string.IsNullOrWhiteSpace(symbol),
"symbol required.");
Contract.Requires<ArgumentException>(
symbol.Equals(symbol.ToUpperInvariant(),
StringComparison.InvariantCulture),
"symbol must be in uppercase.");
//return default(Task<Stock>);
throw new NotImplementedException();
}
public Task UpdateStockPricesAsync()
{
// If this method doesn't mutate state within
// the class, consider marking it [Pure].
//return default(Task);
throw new NotImplementedException();
}
public Task UpdateStockValuesAsync(DateTime date)
{
Contract.Requires<ArgumentOutOfRangeException>(date <= DateTime.Today,
"date cannot be in the future.");
//return default(Task);
throw new NotImplementedException();
}
}
// YOUR NEW STUBS PROJECT
/////////////////////////////////////////////////////////////////
using YourNamespaceWithInterface;
// To make things simpler, use the same namespace as your interface,
// but put '.Stubs' on the end of it.
namespace YourNamespaceWithInterface.Stubs
{
// Again, this is a stub--it doesn't have to do anything
// useful. So, if you're not going to use this stub for
// checking logic and only use it for contract condition
// checking, it's OK to return null--as you're not actually
// depending on the return values of methods (unless you
// have Contract.Ensures(bool condition) on any methods--
// in which case, it will matter).
public class StockDataProviderStub : IStockDataProvider
{
public Task BringStockHistoryCurrentAsync()
{
return null;
}
public Task<Stock> GetStockAsync(string symbol)
{
Contract.Requires<ArgumentException>(
!string.IsNullOrWhiteSpace(symbol),
"symbol required.");
Contract.Requires<ArgumentException>(
symbol.Equals(symbol.ToUpperInvariant(),
StringComparison.InvariantCulture),
"symbol must be in uppercase.");
return null;
}
public Task UpdateStockPricesAsync()
{
return null;
}
public Task UpdateStockValuesAsync(DateTime date)
{
Contract.Requires<ArgumentOutOfRangeException>(
date <= DateTime.Today,
"date cannot be in the future.");
return null;
}
}
}
// IN YOUR UNIT TEST PROJECT
//////////////////////////////////////////////////////////////////
using YourNamespaceWithInteface.Stubs
[TestClass]
public class StockDataProviderTests
{
private IStockDataProvider _stockDataProvider;
[TestInitialize]
public void Initialize()
{
_stockDataProvider = new StockDataProviderStub();
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public async Task GetStockAsyncSymbolEmptyThrowsArgumentException()
{
//act
await provider.GetStockAsync(string.Empty);
//assert
Assert.Fail("Should have thrown ArgumentException");
}
}
通过创建包含接口(interface) stub 实现的项目并在 stub 项目上启用执行运行时契约(Contract)检查,您现在可以在单元测试中测试契约(Contract)条件。
我还强烈建议您阅读一些有关单元测试和各种测试替身的角色的文章。有一次,我认为模拟、 stub 、假货不是一回事。好吧,是的,不是。答案有点微妙。不幸的是,像 MoQ 这样的库虽然很棒!但无济于事,因为它们往往会混淆您在使用这些库时在测试中实际使用的内容。同样,这并不是说它们没有帮助、没有用或没有用处——只是说您在使用这些库时需要准确了解您使用的是什么。我可以提出的建议是 xUnit Test Patterns .还有一个网站:http://xunitpatterns.com/ .
关于c# - 证明我的契约(Contract)正在验证正确的事情,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34823531/
我有一个加号/减号按钮,希望用户不能选择超过 20 个但不知道如何让它工作。我尝试使用 min="1"max="5 属性,但它们不起作用。这是我的代码和一个 fiddle 链接。https://jsf
我正在尝试复制顶部底部图,如示例 here但它没有正确渲染(紫色系列有 +ve 和 -ve 值,绿色为负值)留下杂乱的人工制品。我也在努力创建一个玩具示例来复制这个问题,所以我希望尽管我缺乏数据,但有
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 这个问题似乎与 help center 中定义的范围内的编程无关。 . 已关闭 6 年前。 社区去年审查了是
这个问题在这里已经有了答案: Adding two positive integers gives negative answer.Why? (4 个答案) 关闭 5 年前。 我遇到了一个奇怪的问题
有谁知道如何将字符串值类型 -4,5 或 5,4 转换为 double -4.5 或 5.4? 最佳答案 只需使用 Double.parseDouble(Locale, String); 糟糕,我很困
我正在尝试根据 TextBlob 分类插入一个仅包含“正”或“负”字符串的新数据框列:对于我的 df 的第一行,结果是 ( pos , 0.75, 0.2499999999999997)我想要' 正
我对 VBA 非常陌生,无法理解如何在一个循环中完成 2 个任务。我非常感谢您的帮助。 我已经能够根据第 3 列中的数据更改第 2 列中的数值,但我不明白如何将负值的字体更改为红色。 表格的大小每月都
欢迎, 我正在使用 jquery 通过 POST 发送表单。 这就是我获得值(value)的方式。 var mytext = $("#textareaid").val(); var dataStrin
double d = 0; // random decimal value with it's integral part within the range of Int32 and always p
我有这个字符串: var a='abc123#xyz123'; 我想构建 2 个正则表达式替换函数: 1) 用 '*' 替换所有确实有 future '#'的字符(不包括'#') 所以结果应该是这样的
我正在使用 DialogFragment。当用户从 Gmail 平板电脑应用程序的屏幕与下面示例图片中的编辑文本进行交互时,我希望正面和负面按钮保持在键盘上方。 在我的尝试中不起作用,这是我的 Dia
从组装艺术一书中,我复制了这句话: In the two’s complement system, the H.O. bit of a number is a sign bit. If the H.O
是否有更好更优雅的方法来实现下面的简单代码(diffYear、A 和 B 是数字): diffYear = yearA - yearB; if (diffYear == 0) { A = B
我正在设计一种语言,并尝试确定 true 应该是 0x01 还是 0xFF。显然,所有非零值都将转换为 true,但我正在尝试确定确切的内部表示。 每种选择的优点和缺点是什么? 最佳答案 没关系,只要
在我的 dialogfragment 类的 OnCreateDialog 中,我正在这样做: AlertDialog.Builder builder = new AlertDialog.Builder
这个问题在这里已经有了答案: Resolving ambiguous overload on function pointer and std::function for a lambda usin
我偶然发现了一个奇怪的 NSDecimalNumber 行为:对于某些值,调用 integerValue、longValue、longLongValue 等,返回意想不到的值(value)。示例: l
这个问题在这里已经有了答案: Resolving ambiguous overload on function pointer and std::function for a lambda using
我有这个正则表达式来测试用户输入是否有效: value.length === 0 || value === '-' || (!isNaN(parseFloat(value)) && /^-?\d+\.
我想用高斯混合模型拟合数据集,数据集包含大约 120k 个样本,每个样本有大约 130 个维度。当我使用 matlab 执行此操作时,我运行脚本(簇号为 1000): gm = fitgmdist(d
我是一名优秀的程序员,十分优秀!