将ApplicationIdleDetectionMode设为Disabled可以让程序运行在锁定屏幕下,将UserIdleDetectionMode设为Disabled可以让程序一直运行而不会休眠!
可以调用这个方法,但它在模拟器中总是返回true,所以要自己判断下是否在模拟器中:
bool hasNetworkConnection = NetworkInterface.GetIsNetworkAvailable();
更详细的信息可以通过如下代码,不过它会block ui线程,所以最好另开个thread调用!
var interfaceType = NetworkInterface.NetworkInterfaceType;
这几天在研究如何进行异步测试, silverlight unit test framework提供了相应支持,异步在这个framework中的含义就是把一些额外的任务排队并稍后执行。 比如有一个request()方法是异步的,一般情况下呼叫这个方法之后无法直接测试返回的结果;但在测试方法TestRequest()中,呼叫request()后可以加入一些额外任务到一个队列中,在退出TestRequest()后执行这些任务。说起来有些拗口,看下图会明白些:

一般运用下面四个方法添加任务到队列中:
EnqueueTestComplete() – 添加一个TestComplete()到队列中,这个方法告知framework测试结束了。
EnqueueCallback() – 添加一个任务到队列
EnqueueConditional() – 添加一个条件判断到队列,如果为true才继续执行
EnqueueDelay() – 添加一些等待时间到队列
下图演示了如何实际使用:

下面回到项目中,看看具体如何做:
1.添加一个MockSinaService:
代码
public class MockSinaService : ISinaService
{
public List<Status> StatusList{get;set;}
public void GetStatuses(System.Action<IEnumerable<Status>> onGetStatusesCompleted = null, System.Action<System.Exception> onError = null, System.Action onFinally = null)
{
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(2);
timer.Tick += delegate(object sender, EventArgs e)
{
Status testStatus =
new Status
{
Text = "this is test status",
CreatedAt = DateTime.Now
};
onGetStatusesCompleted(new List<Status>() { testStatus });
timer.Stop();
};
timer.Start();
}
}
2. 添加一个公共属性SinaService到MainPageViewModel中,这样测试项目可以把它指向mock类
代码
public ISinaService SinaService
{
get
{
if (_sinaService == null)
{
_sinaService = new SinaService();
}
return _sinaService;
}
set
{
if (value != _sinaService)
{
_sinaService = value;
}
}
}
代码
public void RefreshHomeList()
{
Trace.DetailMsg("RefreshHomeList");
IsHomeRefreshing = true;
SinaService.GetStatuses(
delegate(IEnumerable<Status> statuses)
{
IsHomeRefreshing = false;
foreach (Status status in statuses)
{
HomeList.Add(status);
}
},
delegate(Exception exception)
{
IsHomeRefreshing = false;
});
}
代码
[TestInitialize]
public void Initialize()
{
_mainViewModel = new MainPageViewModel();
_mockSinaService = new MockSinaService();
_mainViewModel.SinaService = _mockSinaService;
}
代码
[TestMethod]
[Asynchronous]
public void Refresh_HomeList_Success()
{
bool isHomeListRefreshed = false;
_mainViewModel.HomeList.CollectionChanged +=
(s, e) =>
{
isHomeListRefreshed = true;
Assert.AreEqual(NotifyCollectionChangedAction.Add, e.Action);
Assert.AreEqual(1, e.NewItems.Count, "only should be +1 item");
};
_mainViewModel.RefreshHomeList();
EnqueueConditional(() => isHomeListRefreshed);
EnqueueCallback(() => Assert.AreEqual(_mainViewModel.HomeList.Count, 1, "Expected non-empty products list."));
EnqueueTestComplete();
注意这里的Asynchronous关键字,而且要让MainPageViewModelTests这个类继承SilverlightTest。
最后测试结果如下:

References:
1. silverlight2-unit-testing by jeff wilcox
2. Asynchronous test support – Silverlight unit test framework and the UI thread by jeff wilcox
3. Silverlight Unit Testing, RhinoMocks, Unity and Resharper by Justin Angel
想了一个礼拜,究竟是先实现些具体功能呢,还是继续完善TDD,还是先后者吧:具体功能无非就是些api的调用,wp7的twiter或者围脖的例子也有一些了,也不少我这一篇:)
1. 准备几个基本类,status,user和SinaService接口
代码
public class User
{
public ulong Id { get; set; }
public string ScreenName { get; set; }
public string ImageUrl { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public int Followers { get; set; }
public int Friends { get; set; }
public DateTime CreatedAt { get; set; }
}
public class Status
{
public Status()
{
User = new User();
}
public DateTime CreatedAt { get; set; }
public ulong Id { get; set; }
public string Text { get; set; }
public string Source { get; set; }
public User User { get; set; }
public bool Favorited { get; set; }
public int RetweetCount { get; set; }
public bool Retweeted { get; set; }
}
/// <summary>
/// A simple Sina service interface
/// </summary>
public interface ISinaService
{
/// <summary>
/// Get statuses from sina weibo
/// </summary>
/// <param name="onGetStatusesCompleted">the callback method</param>
/// <param name="onError"></param>
/// <param name="onFinally"></param>
void GetStatuses(Action<IEnumerable<Status>> onGetStatusesCompleted = null, Action<Exception> onError = null, Action onFinally = null);
}
status和user类的相应测试俺就偷懒不写了,读者有兴趣可以自己试试!
2. 修改下MainPageViewModel, 加入SinaService接口和HomeList,前者用来调用sina的接口取得数据,后者是最新微博的容器。注意把它们设置为internal,方便测试项目调用,还要添加如下一行在AssemblyInfo.cs中:
[assembly: InternalsVisibleTo("WeiBo7.Test")]
代码
public class MainPageViewModel : ViewModelBase
{
internal readonly ISinaService SinaService;
private bool _isHomeRefreshing = false;
public MainPageViewModel()
{
SinaService = new SinaService();
HomeList = new ObservableCollection<Status>();
}
public ObservableCollection<Status> HomeList { get; internal set; }
/// <summary>
/// Indicates whether there is an indeterminate operation in progress
/// </summary>
public bool IsHomeRefreshing
{
get { return _isHomeRefreshing; }
set
{
if (value != _isHomeRefreshing)
{
_isHomeRefreshing = value;
OnNotifyPropertyChanged("IsHomeRefreshing");
}
}
}
}
3. 现在转到测试项目,先加入一个TestInitialize方法,在所有的测试方法前做些准备工作:
private MainPageViewModel _mainViewModel;
[TestInitialize]
public void Initialize()
{
_mainViewModel = new MainPageViewModel();
}
还可以加入一个[TestCleanup]方法:
[TestCleanup]
public void CleanUp()
{
}
目前没干什么事情,但对于任何一个测试方法,执行顺序是TestInitialize->test method->TestCleanup, 这样保证每个测试方法是彼此独立,互不影响,不用在每个测试方法里面都初始化,节省些代码。
4. 在MainPageViewModelTests先添加一个negative test, 看下这个类如何应对NullInstance
代码
[TestMethod]
[ExpectedException(typeof(NullReferenceException))]
public void NullInstance()
{
_mainViewModel = null;
bool x = _mainViewModel.IsHomeRefreshing;
}
5. 测试初始化是否成功
代码
[TestMethod]
public void Init_MainPageViewModel_Success()
{
Assert.IsNotNull(_mainViewModel);
Assert.IsNotNull(_mainViewModel.HomeList);
Assert.IsNotNull(_mainViewModel.SinaService);
Assert.IsInstanceOfType(_mainViewModel, typeof(INotifyPropertyChanged));
}
6. 测试当添加一个item到HomeList时,是否CollectionChanged被fire
代码
public void AddItem_HomeList_RaisesCollectionChanged()
{
bool changeObserved = false;
_mainViewModel.HomeList.CollectionChanged +=
(s, e) =>
{
changeObserved = true;
Assert.AreEqual(NotifyCollectionChangedAction.Add, e.Action);
Assert.AreEqual(1, e.NewItems.Count, "only should be +1 item");
};
_mainViewModel.HomeList.Add(new Status());
Assert.IsTrue(changeObserved);
}

