在ModelState.AddModelError上对控制器单元测试失败

时间:2016-02-23 13:28:54

标签: asp.net-mvc unit-testing

我正在测试一个MVC控制器。虽然我可以在测试运行后断言模型状态,但如果在将错误添加到ModelState时我可以使测试失败,我会更喜欢它。我的想法是模拟ModelState,但它没有setter。

有人知道如何在单元测试中听取ModelState.AddModelError吗?

2 个答案:

答案 0 :(得分:1)

围绕ModelState类包装类:

public class ModelStateWrapper : IValidationDictionary
{
    private ModelStateDictionary ModelStateDictionary;

    public ModelStateWrapper(ModelStateDictionary modelStateDictionary)
    {
        this.ModelStateDictionary = modelStateDictionary;
    }

    /// <summary>
    /// Adds a validation error to the model state dictionary
    /// </summary>
    /// <param name="key">The key for the field</param>
    /// <param name="errorMessage">The error message</param>
    public void AddError(string key, string errorMessage)
    {
        this.ModelStateDictionary.AddModelError(key, errorMessage);
    }

    /// <summary>
    /// Determines whether there are error messages in the model state dictionary
    /// </summary>
    public bool IsValid
    {
        get
        {
            return this.ModelStateDictionary.IsValid;
        }
    }
}

界面可能如下所示:

public interface IValidationDictionary
{
    void AddError(string key, string errorMessage);
    bool IsValid { get; }
}

然后在您的类中,在控制器的构造函数中分配ModelState:

public HomeController : IController
{
    private IValidationDictionary validationDictionary;

    public HomeController()
    {
        this.validationDictionary = new ModelStateWrapper(this.ModelState);
    }

    public ActionResult Index()
    {
        this.myService.CallSomething(this.validationDictionary)
    }
}

当您将this.validationDictionary的实例传递给服务时,它会从您的控制器中扩充原始ModelState

答案 1 :(得分:1)

修正了它。首次尝试使用:

var f = typeof(ViewDataDictionary).GetField("_modelState", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField);
var modelStateDictionary = MockRepository.GenerateMock<ModelStateDictionaryMock>();
f.SetValue(controller.ViewData, modelStateDictionary);

public abstract class ModelStateDictionaryMock : ModelStateDictionary
{
    public abstract int Count { get; }

    public abstract bool IsReadOnly { get; }

    public abstract bool IsValid { get; }

    public abstract ICollection<string> Keys { get; }

    public abstract ICollection<ModelState> Values { get; }

    public abstract ModelState this[string key] { get; set; }

    public abstract void Add(KeyValuePair<string, ModelState> item);

    public abstract void Add(string key, ModelState value);

    public abstract void AddModelError(string key, Exception exception);

    public abstract void AddModelError(string key, string errorMessage);

    public abstract void Clear();

    public abstract bool Contains(KeyValuePair<string, ModelState> item);

    public abstract bool ContainsKey(string key);

    public abstract void CopyTo(KeyValuePair<string, ModelState>[] array, int arrayIndex);

    public abstract IEnumerator<KeyValuePair<string, ModelState>> GetEnumerator();

    public abstract bool IsValidField(string key);

    public abstract void Merge(ModelStateDictionary dictionary);

    public abstract bool Remove(KeyValuePair<string, ModelState> item);

    public abstract bool Remove(string key);

    public abstract void SetModelValue(string key, ValueProviderResult value);

    public abstract bool TryGetValue(string key, out ModelState value);
}

然而,由于以这种方式进行模拟无效,因此无法正常工作。所以我用Microsoft Fakes解决了它:

System.Web.Mvc.Fakes.ShimModelStateDictionary.AllInstances.AddModelErrorStringString =
    (dictionary, key, errorMessage) =>
    {
        Assert.Fail(errorMessage);
    };