0

我正在使用 MonoTouch 编写一个 iOS 应用程序,该应用程序有很多信息要在很多不同的屏幕上输入。信息 A、B 和 C 可能会在屏幕 1 上输入,但我需要在屏幕 3 上显示信息 B 以及信息 X、Y、Z 等的输入。用户可以从屏幕 1 跳到屏幕 7,然后返回屏幕 3。

因此,我创建了一个静态类来接收所有信息(通过 NSNotifications)并缓存它。它还监听来自各个屏幕的信息请求(同样,通过 NSNotifications),从缓存中打包相关信息(在私有静态方法中),并发出另一个通知作为响应。此类应始终具有最新信息,因此无论用户跳转到哪个屏幕都应如此。

public static class InformationFlowController
{
    static List<NSObject> _observers;
    static Dictionary<SomeEnum, Object> _data;

    static InformationFlowController()
    {
        _observers = new List<NSObject>();
        _data = new Dictionary<SomeEnum, Object>();

        _observers.Add(NSNotificationCenter.DefaultCenter.AddObserver(Notifications.PayloadA, HandlePayloadA));
        ...
        _observers.Add(NSNotificationCenter.DefaultCenter.AddObserver(Notifications.RequestA, HandleRequestA));
    }

    // receive information A
    static void HandlePayloadA(NSNotification ntf)
    {
        var infoA = (InfoA)ntf.Object;
        if (A == null) { /* throw an exception */ }

        if (_data.ContainsKey(EnumA))
        {
            _data.Remove(EnumA);
        }

        _data.Add(EnumA, infoA);
    }

    // receive request for info A
    static void HandleRequestA(NSNotification ntf)
    {
        InfoA info = null;

        if (_data.ContainsKey(EnumA))
        {
            info = _data.GetValue(EnumA);
        }

        // its up to the receiver what it wants to do if it gets null
        NSNotificationCenter.DefaultCenter.PostNotificationName(Notifications.ResponseA, info);
    }
}

这一切都很好。

我的问题是如何进行单元/集成测试? 由于 app/policy/yadda-yadda 的性质,我必须有 ~100% 的代码覆盖率。这是我尝试过的和遇到的。

我试图为此编写集成测试的问题是确保如果我请求信息 A,我会得到信息 A 的响应。

[Test]
public void HandleRequestForATest()
{
    // setup the data in the IFC here

    NSNotificationCenter.DefaultCenter.AddObserver(Notifications.ResponseA, delegate(NSNotification ntf)
    {
        var info = ntf.Object as InfoA;
        Assert.IsNotNull(info, "Info is null!");
        // some other asserts to check the data within info
    }

    NSNotificationCenter.DefaultCenter.PostNotificationName(Notifications.RequestA, null);        
}

测试以 0 个断言通过。我放了一个断点var info = ntf.Object as InfoA;,它永远不会被触及。我想也许它在收到响应之前就被 GC 处理了,但是我在IFC.HandleRequestA方法中设置了一个断点,它永远不会被调用。通过检查,我可以看到通知正在向 DefaultCenter 注册,但它们似乎没有被解雇。

除此之外,我如何测试以确保在一定时间内收到通知,如果没有,则测试失败?我尝试添加一个类变量:

bool assertsCompleted = false;

并修改测试:

[Test]
public void HandleRequestForATest()
{
    // setup the data in the IFC here

    NSNotificationCenter.DefaultCenter.AddObserver(Notifications.ResponseA, delegate(NSNotification ntf)
    {
        var info = ntf.Object as InfoA;
        Assert.IsNotNull(info, "Info is null!");
        // some other asserts to check the data within info
        assertsCompleted = true;
    }

    NSNotificationCenter.DefaultCenter.PostNotificationName(Notifications.RequestA, null);        

    NSTimer.CreateTimer(2, delegate
    {
        if (!assertsCompleted)
        {
            Assert.IsTrue(false, "Notification not received or Asserts not complete!");
        }
    } 
}

我放了一个断点if (!assertsCompleted),也没有被击中。

请帮忙!:)

EDIT2 我将 IFC 类从静态更改为非静态(以及方法),并解决了未调用处理程序的问题。这意味着我必须在 AppDelegate 和 TestFixtureSetUp 中实例化它,但我认为我们可以忍受这一点。 /EDIT2

4

1 回答 1

2

你可以尝试这样的事情:

[Test]
public void HandleRequestForATest()
{
    // setup the data in the IFC here

    var resetEvent = new ManualResetEvent(false);
    InfoA info = null;

    var observer = NSNotificationCenter.DefaultCenter.AddObserver(Notifications.ResponseA, delegate(NSNotification ntf)
    {
        info = ntf.Object as InfoA;
        resetEvent.Set();
    }

    NSNotificationCenter.DefaultCenter.PostNotificationName(Notifications.RequestA, null);        

    Assert.IsTrue(resetEvent.WaitOne(250), "Reset event timed out!");
    Assert.IsNotNull(info, "Info is null!");
    // some other asserts to check the data within info

    //Make sure you do this! I'd put in try-finally block
    NSNotificationCenter.DefaultCenter.RemoveObserver(observer);
}
于 2013-04-12T17:42:54.587 回答