Mercurial > silverbladetech
changeset 97:1adc1ae981ea
Tests added to SilverlightValidation.Tests
line wrap: on
line diff
--- a/SilverlightValidation/SilverlightValidation.PL/ViewModels/UserViewModel.cs Sat May 05 13:29:56 2012 +0100 +++ b/SilverlightValidation/SilverlightValidation.PL/ViewModels/UserViewModel.cs Sat May 05 16:39:00 2012 +0100 @@ -27,6 +27,9 @@ public UserViewModel(UserModel model, UserModelValidator validator) { + if (model == null) throw new ArgumentNullException("model"); + if (validator == null) throw new ArgumentNullException("validator"); + _validator = validator; _data = model; _backup = model.Clone();
--- a/SilverlightValidation/SilverlightValidation.Tests/SilverlightValidation.Tests.csproj Sat May 05 13:29:56 2012 +0100 +++ b/SilverlightValidation/SilverlightValidation.Tests/SilverlightValidation.Tests.csproj Sat May 05 16:39:00 2012 +0100 @@ -31,11 +31,17 @@ <WarningLevel>4</WarningLevel> </PropertyGroup> <ItemGroup> + <Reference Include="FluentValidation"> + <HintPath>..\Libs\FluentValidation.dll</HintPath> + </Reference> <Reference Include="nunit.framework, Version=2.6.0.12051, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL"> - <HintPath>..\packages\NUnit.2.6.0.12054\lib\nunit.framework.dll</HintPath> + <HintPath>..\Libs\nunit.framework.dll</HintPath> </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> + <Reference Include="System.Windows"> + <HintPath>..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v5.0\System.Windows.dll</HintPath> + </Reference> <Reference Include="System.Xml.Linq" /> <Reference Include="System.Data.DataSetExtensions" /> <Reference Include="Microsoft.CSharp" /> @@ -43,12 +49,23 @@ <Reference Include="System.Xml" /> </ItemGroup> <ItemGroup> + <Compile Include="TestSupport\PropertySupport.cs" /> + <Compile Include="TestSupport\CommandCanExecuteAssertHelper.cs" /> + <Compile Include="TestSupport\CommandCanExecuteChangedEventWatcher.cs" /> + <Compile Include="TestSupport\Disposable.cs" /> + <Compile Include="TestSupport\NotifyPropertyChangedAssertHelper.cs" /> + <Compile Include="TestSupport\NotifyPropertyChangedEventWatcher.cs" /> + <Compile Include="ViewModels\NotifyPropertyChangedTester.cs" /> <Compile Include="ViewModels\UserListViewModelTests.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="ViewModels\UserViewModelTests.cs" /> + <Compile Include="ViewModels\ViewModelBaseTests.cs" /> </ItemGroup> <ItemGroup> - <None Include="packages.config" /> + <ProjectReference Include="..\SilverlightValidation.PL\SilverlightValidation.PL.csproj"> + <Project>{13B5F568-F402-4A2A-9A23-0FDF0B5564E3}</Project> + <Name>SilverlightValidation.PL</Name> + </ProjectReference> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightValidation/SilverlightValidation.Tests/TestSupport/CommandCanExecuteAssertHelper.cs Sat May 05 16:39:00 2012 +0100 @@ -0,0 +1,162 @@ +using System; +using System.Windows.Input; +using NUnit.Framework; + +namespace SilverlightValidation.Tests.TestSupport +{ + public static class CommandCanExecuteAssertHelper + { + #region Assert methods (using helper) + + #region Can/cannot execute + + /// <summary> + /// Raises an assertion if the supplied command's CanExecute method returns false. + /// </summary> + /// <remarks> + /// CanExecute is passed null. + /// </remarks> + /// <param name="command">The command.</param> + public static void AssertCanExecute(this ICommand command) + { + if (command == null) throw new ArgumentNullException("command"); + + Assert.IsTrue(command.CanExecute(null)); + } + + /// <summary> + /// Raises an assertion if the supplied command's CanExecute method returns false. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="command">The command.</param> + /// <param name="value">A value to pass to CanExecute.</param> + public static void AssertCanExecute<T>(this ICommand command, T value) + { + if (command == null) throw new ArgumentNullException("command"); + + Assert.IsTrue(command.CanExecute(value)); + } + + /// <summary> + /// Raises an assertion if the supplied command's CanExecute method returns true. + /// </summary> + /// <remarks> + /// CanExecute is passed null. + /// </remarks> + /// <param name="command">The command.</param> + public static void AssertCannotExecute(this ICommand command) + { + if (command == null) throw new ArgumentNullException("command"); + + Assert.IsTrue(!command.CanExecute(null)); + } + + /// <summary> + /// Raises an assertion if the supplied command's CanExecute method returns true. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="command">The command.</param> + /// <param name="value">A value to pass to CanExecute.</param> + public static void AssertCannotExecute<T>(this ICommand command, T value) + { + if (command == null) throw new ArgumentNullException("command"); + + Assert.IsTrue(!command.CanExecute(value)); + } + + #endregion + + #region Assert is raised/Assert is not raised (CommandCanExecute) + + /// <summary> + /// Raises an assertion if the action is invoked and the supplied command + /// raises a CommandCanExecuteChanged event. + /// </summary> + /// <param name = "command">The command.</param> + /// <param name = "action">The action to invoke.</param> + public static void AssertIsNotRaised(this ICommand command, Action action) + { + Assert.IsTrue(IsNotRaised(command, action)); + } + + /// <summary> + /// Raises an assertion if the action is invoked and the supplied command + /// does not raise at least one CommandCanExecuteChanged event. + /// </summary> + /// <param name = "command">The command.</param> + /// <param name = "action">The action to invoke.</param> + public static void AssertIsRaised(this ICommand command, Action action) + { + Assert.IsTrue(IsRaised(command, action)); + } + + /// <summary> + /// Raises an assertion if the action is invoked and the supplied command + /// does raise the number of CommandCanExecuteChanged events as defined by the + /// supplied predicate. + /// </summary> + /// <param name = "command">The command.</param> + /// <param name = "predicate">The predicate.</param> + /// <param name = "action">The action to invoke.</param> + public static void AssertIsRaised(this ICommand command, Predicate<int> predicate, Action action) + { + Assert.IsTrue(IsRaised(command, predicate, action)); + } + + #endregion + + #endregion + + #region Is raised/Is not raised methods (CommandCanExecute) + + /// <summary> + /// Determines if the specified CommandCanExecuteChanged event is + /// not raised when the supplied action is invoked. + /// </summary> + /// <param name = "command">The command.</param> + /// <param name = "action">The action to invoke.</param> + public static bool IsNotRaised(this ICommand command, Action action) + { + return !IsRaised(command, action); + } + + /// <summary> + /// Determines if the specified CommandCanExecuteChanged event is + /// raised one or more times when the supplied action is invoked. + /// </summary> + /// <param name = "command">The command.</param> + /// <param name = "action">The action to invoke.</param> + /// <returns> + /// <c>true</c> if the specified command is raised; otherwise, <c>false</c>. + /// </returns> + public static bool IsRaised(this ICommand command, Action action) + { + return IsRaised(command, count => count > 0, action); + } + + /// <summary> + /// Determines if the specified CommandCanExecuteChanged event is + /// raised the number of times as defined by the predicate when the supplied action is invoked. + /// </summary> + /// <param name = "command">The command.</param> + /// <param name = "predicate">A predicate.</param> + /// <param name = "action">The action to invoke.</param> + /// <returns> + /// <c>true</c> if the specified command's CanExecuteChanged event is raised; otherwise, <c>false</c>. + /// </returns> + public static bool IsRaised(this ICommand command, Predicate<int> predicate, Action action) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + if (action == null) throw new ArgumentNullException("action"); + + using (var watcher = new CommandCanExecuteChangedEventWatcher(command)) + { + action(); + + return predicate(watcher.RaisedCount); + } + } + + #endregion + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightValidation/SilverlightValidation.Tests/TestSupport/CommandCanExecuteChangedEventWatcher.cs Sat May 05 16:39:00 2012 +0100 @@ -0,0 +1,51 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Input; + +namespace SilverlightValidation.Tests.TestSupport +{ + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] + internal sealed class CommandCanExecuteChangedEventWatcher : Disposable + { + private ICommand _command; + + /// <summary> + /// Initializes a new instance of the <see cref = "CommandCanExecuteChangedEventWatcher" /> class. + /// </summary> + /// <param name = "command">The command.</param> + public CommandCanExecuteChangedEventWatcher(ICommand command) + { + if (command == null) + { + throw new ArgumentNullException("command"); + } + + _command = command; + _command.CanExecuteChanged += CommandCanExecuteChanged; + } + + private void CommandCanExecuteChanged(object sender, EventArgs e) + { + RaisedCount++; + } + + /// <summary> + /// Gets the number of times CanExecuteChanged was raised for the monitored command. + /// </summary> + public int RaisedCount { get; private set; } + + /// <summary> + /// Releases unmanaged and - optionally - managed resources + /// </summary> + /// <param name = "disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + protected override void Dispose(bool disposing) + { + if (_command != null) + { + var command = _command; + _command = null; + command.CanExecuteChanged -= CommandCanExecuteChanged; + } + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightValidation/SilverlightValidation.Tests/TestSupport/Disposable.cs Sat May 05 16:39:00 2012 +0100 @@ -0,0 +1,32 @@ +using System; + +namespace SilverlightValidation.Tests.TestSupport +{ + public abstract class Disposable : IDisposable + { + /// <summary> + /// A base class for disposable classes. + /// </summary> + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// <summary> + /// Releases unmanaged and - optionally - managed resources + /// </summary> + /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + protected abstract void Dispose(bool disposing); + + /// <summary> + /// Releases unmanaged resources and performs other cleanup operations before the + /// <see cref="Disposable"/> is reclaimed by garbage collection. + /// </summary> + ~Disposable() + { + Dispose(false); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightValidation/SilverlightValidation.Tests/TestSupport/NotifyPropertyChangedAssertHelper.cs Sat May 05 16:39:00 2012 +0100 @@ -0,0 +1,260 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Linq.Expressions; +using NUnit.Framework; + +namespace SilverlightValidation.Tests.TestSupport +{ + /// <summary> + /// A utility class containing methods that verify whether a property changed event + /// is raised/not raised for a specified model property, on invocation of a specified Action. + /// </summary> + /// <remarks> + /// An example of usage in conjunction with NUnit: + /// <code> + /// var model = new CustomerViewModel(); + /// Assert.That(NotifyPropertyChangedAssertHelper.IsRaised(model, () => model.Name = "foo", () => model.Name, count => count == 1) + /// </code> + /// Here the action is model.Name = "foo", + /// the monitored property is the "Name" property, specified by the lambda expression "() => model.Name", + /// and the test for success is "count == 1". + /// </remarks> + public static class NotifyPropertyChangedAssertHelper + { + #region Assert methods (using AssertionProxy) + + #region Is raised + + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static void AssertIsRaised<T, T1>(this T model, Expression<Func<T, T1>> propertyExpression, Action action) + where T : INotifyPropertyChanged + { + Assert.IsTrue(IsRaised(model, propertyExpression, action)); + } + + public static void AssertIsRaised(this INotifyPropertyChanged model, string propertyName, Action action) + { + Assert.IsTrue(IsRaised(model, propertyName, action)); + } + + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static void AssertIsRaised<T, T1>( + this T model, Expression<Func<T, T1>> propertyExpression, Predicate<int> predicate, Action action) + where T : INotifyPropertyChanged + { + Assert.IsTrue(IsRaised(model, propertyExpression, predicate, action)); + } + + public static void AssertIsRaised(this INotifyPropertyChanged model, string propertyName, + Predicate<int> predicate, Action action) + { + Assert.IsTrue(IsRaised(model, propertyName, predicate, action)); + } + + #endregion + + #region Are raised + + public static void AssertAreRaised(this INotifyPropertyChanged model, IEnumerable<string> propertyNames, + Action action) + { + Assert.IsTrue(AreRaised(model, propertyNames, action)); + } + + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static void AssertAreRaised(this INotifyPropertyChanged model, + IDictionary<string, Predicate<int>> criteria, Action action) + { + Assert.IsTrue(AreRaised(model, criteria, action)); + } + + #endregion + + #region Is not raised + + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static void AssertIsNotRaised<T, T1>(this T model, Expression<Func<T, T1>> propertyExpression, + Action action) + where T : INotifyPropertyChanged + { + Assert.IsTrue(IsNotRaised(model, propertyExpression, action)); + } + + public static void AssertIsNotRaised(this INotifyPropertyChanged model, Action action, string propertyName) + { + Assert.IsTrue(IsNotRaised(model, action, propertyName)); + } + + #endregion + + #region Are not raised + + public static void AssertAreNotRaised(this INotifyPropertyChanged model, IEnumerable<string> propertyNames, + Action action) + { + Assert.IsTrue(AreNotRaised(model, propertyNames, action)); + } + + #endregion + + #endregion + + #region Is and Are methods + + #region Is raised + + /// <summary> + /// Determines whether the specified property changed event is raised. + /// </summary> + /// <returns> + /// <c>true</c> if the specified property changed event is raised at least once; otherwise, <c>false</c>. + /// </returns> + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters"), + SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static bool IsRaised<T, T1>(this T model, Expression<Func<T, T1>> propertyExpression, Action action) + where T : INotifyPropertyChanged + { + return IsRaised(model, PropertySupport.ExtractPropertyName(propertyExpression), action); + } + + /// <summary> + /// Determines whether the specified property changed event is raised. + /// </summary> + /// <returns> + /// <c>true</c> if the specified property changed event is raised at least once; otherwise, <c>false</c>. + /// </returns> + public static bool IsRaised(this INotifyPropertyChanged model, string propertyName, Action action) + { + return AreRaised(model, new[] {propertyName}, action); + } + + /// <summary> + /// Determines whether the specified property changed event is raised. + /// </summary> + /// <returns> + /// <c>true</c> if the specified property changed event count satisfies the supplied predicate; otherwise, <c>false</c>. + /// </returns> + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters"), + SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static bool IsRaised<T, T1>(this T model, Expression<Func<T, T1>> propertyExpression, + Predicate<int> predicate, Action action) + where T : INotifyPropertyChanged + { + return IsRaised(model, PropertySupport.ExtractPropertyName(propertyExpression), predicate, action); + } + + /// <summary> + /// Determines whether the specified property changed event is raised. + /// </summary> + /// <returns> + /// <c>true</c> if the specified property changed event count satisfies the supplied predicate; otherwise, <c>false</c>. + /// </returns> + public static bool IsRaised(this INotifyPropertyChanged model, string propertyName, Predicate<int> predicate, + Action action) + { + if (action == null) + { + throw new ArgumentNullException("action"); + } + + if (predicate == null) + { + throw new ArgumentNullException("predicate"); + } + + return AreRaised(model, new Dictionary<string, Predicate<int>> {{propertyName, predicate}}, action); + } + + #endregion + + #region Are raised + + /// <summary> + /// Determines whether each of the specified properties has at least one NotifyPropertyChanged event raised. + /// </summary> + public static bool AreRaised(this INotifyPropertyChanged model, IEnumerable<string> propertyNames, Action action) + { + return AreRaised(model, propertyNames.ToDictionary<string, string, Predicate<int>>(p => p, p => c => c > 0), + action); + } + + /// <summary> + /// Determines whether the specified property changed events are raised. + /// </summary> + /// <returns> + /// <c>true</c> if the specified property changed event count satisfies the supplied predicate for each property; otherwise, <c>false</c>. + /// </returns> + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static bool AreRaised(this INotifyPropertyChanged model, IDictionary<string, Predicate<int>> criteria, + Action action) + { + if (action == null) + { + throw new ArgumentNullException("action"); + } + + if (criteria == null) + { + throw new ArgumentNullException("criteria"); + } + + using (var watcher = new NotifyPropertyChangedEventWatcher(model)) + { + action(); + + return criteria.All(pair => pair.Value(watcher.GetRaisedCount(pair.Key))); + } + } + + #endregion + + #region Is not raised + + /// <summary> + /// Determines whether the specified property changed event is not raised. + /// </summary> + /// <returns> + /// <c>true</c> if the specified property changed event is raised zero times; otherwise, <c>false</c>. + /// </returns> + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters"), + SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static bool IsNotRaised<T, T1>(this T model, Expression<Func<T, T1>> propertyExpression, Action action) + where T : INotifyPropertyChanged + { + return IsNotRaised(model, action, PropertySupport.ExtractPropertyName(propertyExpression)); + } + + /// <summary> + /// Determines whether the specified property changed event is not raised. + /// </summary> + /// <returns> + /// <c>true</c> if the specified property changed event is raised zero times; otherwise, <c>false</c>. + /// </returns> + public static bool IsNotRaised(this INotifyPropertyChanged model, Action action, string propertyName) + { + return AreNotRaised(model, new[] {propertyName}, action); + } + + #endregion + + #region Are not raised + + /// <summary> + /// Determines whether all of the specified properties have changed events raised. + /// </summary> + public static bool AreNotRaised(this INotifyPropertyChanged model, IEnumerable<string> propertyNames, + Action action) + { + return AreRaised(model, + propertyNames.ToDictionary<string, string, Predicate<int>>(p => p, p => c => c == 0), + action); + } + + #endregion + + #endregion + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightValidation/SilverlightValidation.Tests/TestSupport/NotifyPropertyChangedEventWatcher.cs Sat May 05 16:39:00 2012 +0100 @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq.Expressions; + +namespace SilverlightValidation.Tests.TestSupport +{ + /// <summary> + /// A helper class used by NotifyPropertyChangedAssertHelper to monitor PropertyChangedEvent notifications. + /// </summary> + public class NotifyPropertyChangedEventWatcher : Disposable + { + private readonly Dictionary<string, int> _raisedCounts = new Dictionary<string, int>(); + + // A reference to the instance of the class implementing INotifyPropertyChanged + private INotifyPropertyChanged _model; + + /// <summary> + /// Initializes a new instance of the <see cref = "NotifyPropertyChangedEventWatcher" /> class. + /// </summary> + /// <param name = "model">The model.</param> + public NotifyPropertyChangedEventWatcher(INotifyPropertyChanged model) + { + if (model == null) throw new ArgumentNullException("model"); + + _model = model; + _model.PropertyChanged += OnPropertyChanged; + } + + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters"), + SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public int GetRaisedCount<T>(Expression<Func<T>> propertyExpression) + { + return GetRaisedCount(PropertySupport.ExtractPropertyName(propertyExpression)); + } + + public int GetRaisedCount(string propertyName) + { + propertyName = string.IsNullOrEmpty(propertyName) ? string.Empty : propertyName; + + PropertySupport.VerifyPropertyName(_model, propertyName); + + return _raisedCounts.ContainsKey(propertyName) ? _raisedCounts[propertyName] : 0; + } + + private void RecordPropertyChanged(string propertyName) + { + if (_raisedCounts.ContainsKey(propertyName)) + { + _raisedCounts[propertyName]++; + } + else + { + _raisedCounts[propertyName] = 1; + } + } + + /// <summary> + /// The OnPropertyChanged event handler. + /// </summary> + /// <remarks> + /// Records how many times the monitored property name has changed. + /// </remarks> + private void OnPropertyChanged(object sender, PropertyChangedEventArgs eventArgs) + { + // If PropertyName is null or string.Empty then it's the 'all properties' changed event. + RecordPropertyChanged(string.IsNullOrEmpty(eventArgs.PropertyName) ? string.Empty : eventArgs.PropertyName); + } + + /// <summary> + /// Releases unmanaged and - optionally - managed resources + /// </summary> + /// <param name = "disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + protected override void Dispose(bool disposing) + { + if (null != _model) + { + var model = _model; + _model = null; + model.PropertyChanged -= OnPropertyChanged; + } + } + + /// <summary> + /// Traces the event counts. + /// </summary> + [Conditional("DEBUG")] + public void TraceEventCounts() + { + foreach (var pair in _raisedCounts) + { + string name = string.IsNullOrEmpty(pair.Key) ? "all properties" : pair.Key; + int value = pair.Value; + + Debug.WriteLine(string.Format( + CultureInfo.InvariantCulture, @"Property '{0}' raised {1} times.", name, value)); + } + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightValidation/SilverlightValidation.Tests/TestSupport/PropertySupport.cs Sat May 05 16:39:00 2012 +0100 @@ -0,0 +1,61 @@ +using System; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; + +namespace SilverlightValidation.Tests.TestSupport +{ + /// <summary> + /// Utility class with property specific helper methods. + /// </summary> + public static class PropertySupport + { + [Conditional("DEBUG")] + [DebuggerStepThrough] + public static void VerifyPropertyName<T>(T model, string propertyName) + { + // A null or empty string indicates all properties on the object have changed + if (!string.IsNullOrEmpty(propertyName)) + { + var modelType = model.GetType(); + if (modelType.GetProperty(propertyName) == null) + { + throw new ArgumentException(@"Property not found on target type", propertyName); + } + } + } + + /// <summary> + /// Extracts the name of a property from a suitable LambdaExpression. + /// </summary> + /// <param name="propertyExpression">The property expression.</param> + /// <returns></returns> + public static string ExtractPropertyName(LambdaExpression propertyExpression) + { + if (propertyExpression == null) + { + throw new ArgumentNullException("propertyExpression"); + } + + var memberExpression = propertyExpression.Body as MemberExpression; + if (memberExpression == null) + { + throw new ArgumentException(@"Not a member expression", "propertyExpression"); + } + + var property = memberExpression.Member as PropertyInfo; + if (property == null) + { + throw new ArgumentException(@"Not a property", "propertyExpression"); + } + + var getMethod = property.GetGetMethod(true); + if (getMethod.IsStatic) + { + throw new ArgumentException(@"Can't be static", "propertyExpression"); + } + + return memberExpression.Member.Name; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightValidation/SilverlightValidation.Tests/ViewModels/NotifyPropertyChangedTester.cs Sat May 05 16:39:00 2012 +0100 @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using NUnit.Framework; + +namespace SilverlightValidation.Tests.ViewModels +{ + public class NotifyPropertyChangedTester + { + public NotifyPropertyChangedTester(INotifyPropertyChanged viewModel) + { + if (viewModel == null) throw new ArgumentNullException("viewModel"); + + this.Changes = new List<string>(); + + viewModel.PropertyChanged += viewModel_PropertyChanged; + } + + private void viewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + Changes.Add(e.PropertyName); + } + + public List<string> Changes { get; private set; } + + public void AssertChange(int changeIndex, string expectedPropertyName) + { + Assert.IsNotNull(Changes); + + Assert.IsTrue(changeIndex < Changes.Count, + "Changes collection contains '{0}' items and does not have an element at index '{1}'.", + Changes.Count, + changeIndex); + + Assert.AreEqual(expectedPropertyName, + Changes[changeIndex], + "Change at index '{0}' is '{1}' and is not equal to '{2}'.", + changeIndex, + Changes[changeIndex], + expectedPropertyName); + } + } +}
--- a/SilverlightValidation/SilverlightValidation.Tests/ViewModels/UserViewModelTests.cs Sat May 05 13:29:56 2012 +0100 +++ b/SilverlightValidation/SilverlightValidation.Tests/ViewModels/UserViewModelTests.cs Sat May 05 16:39:00 2012 +0100 @@ -1,14 +1,42 @@ -using NUnit.Framework; +using System; +using NUnit.Framework; +using SilverlightValidation.Models; +using SilverlightValidation.Validators; +using SilverlightValidation.ViewModels; namespace SilverlightValidation.Tests.ViewModels { [TestFixture] class UserViewModelTests { + #region constructor tests + [Test] - public void Given_When_Then() + public void Constructor_WhenTwoNulls_ThenArgumentNullExceptionForModel() + { + Assert.Throws<ArgumentNullException>(() => new UserViewModel(null, null), "model"); + } + + [Test] + public void Constructor_WhenFirstParameterIsNull_ThenArgumentNullExceptionForModel() { - Assert.True(true); + Assert.Throws<ArgumentNullException>(() => new UserViewModel(null, new UserModelValidator()), "model"); + } + + [Test] + public void Constructor_WhenSecondParameterIsNull_ThenArgumentNullExceptionForValidator() + { + Assert.Throws<ArgumentNullException>(() => new UserViewModel(new UserModel(), null), "validator"); } + + [Test] + public void DateOfBirth_WhenUpdated_ThenFiresPropertyChangeEvent() + { + var vm = new UserViewModel(new UserModel(), new UserModelValidator()); + var tester = new NotifyPropertyChangedTester(vm); + //tester.AssertChange(); + } + + #endregion } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightValidation/SilverlightValidation.Tests/ViewModels/ViewModelBaseTests.cs Sat May 05 16:39:00 2012 +0100 @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using NUnit.Framework; + +namespace SilverlightValidation.Tests.ViewModels +{ + [TestFixture] + class ViewModelBaseTests + { + + } +}
--- a/SilverlightValidation/SilverlightValidation/App.xaml.cs Sat May 05 13:29:56 2012 +0100 +++ b/SilverlightValidation/SilverlightValidation/App.xaml.cs Sat May 05 16:39:00 2012 +0100 @@ -1,9 +1,5 @@ using System; using System.Windows; -using System.Windows.Controls; -using System.Windows.Navigation; -//using SilverlightGlimpse.Services; -using System.Diagnostics; namespace SilverlightValidation { @@ -20,15 +16,7 @@ private void Application_Startup(object sender, StartupEventArgs e) { - try - { - this.RootVisual = new Views.UserListView(); - //GlimpseService.CreateInstance.Load(this, "Silverlight Glimpse"); - } - catch (Exception ex) - { - //GlimpseService.CreateInstance.DisplayLoadFailure(this, ex, "Glimpse Demo"); - } + this.RootVisual = new Views.UserListView(); } private void Application_Exit(object sender, EventArgs e)