Search code examples
c#validationunit-testingcode-contracts

Proving my contracts are validating the right thing


I have an interface like so:

[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;
}

I have a corresponding contract class like so:

[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);
    }
}

I made a unit test like so:

[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");
    }
}

From what I've read, this should be sufficient to pass the unit test, but upon acting, the unit test fails by not throwing an exception.

I'm not trying to test the contract functionality, but I am interested in testing the validation logic to make sure my requirements are met for concrete implementations of the IStockDataProvider interface.

Am I doing this wrong? How can I verify, using my unit tests, that I have properly specified my inputs?

UPDATE

So, while mocking the interface and testing the validation logic does not seem to work, my concrete class (not inheriting from the abstract) validates the inputs properly in testing. So it may just not be supported in mocking, though I don't quite know why.


Solution

  • For whatever reason, I was not able to get a mocked interface to throw the expected exceptions; that being said, I was able to test the exceptions with every implementation of the interface. It's a bit frustrating, and it seems to fly in the face of the DRY principle, but I was able to unit test these contracts.