【问题标题】:NServiceBus 2.5.0.1442 Unit Testing Bus.CreateInstance<Interface> returning null in some unit testsNServiceBus 2.5.0.1442 单元测试 Bus.CreateInstance<Interface> 在某些单元测试中返回 null
【发布时间】:2014-06-13 02:58:02
【问题描述】:

我在使用 Sagas 进行 NServiceBus 单元测试时遇到了奇怪的行为。

跑步

  • NSB 2.5.0.1442(由于 IMessage 被卡在这里,并且它在多个服务中)
  • NServiceBus.Testing
  • Resharper 8.2 单元测试运行器
  • VS2013更新最新rtm版本/package/thingy

我的解决方案中有三个单元测试项目。其中只有一个引用了 NServiceBus.Testing,因为它是唯一一个测试 Saga。

  1. Messages.Testing
  2. Service.Testing(这个引用NSB.Testing)
  3. ViewModelUpdater.Testing

单独运行单元测试项目一切正常。

同时运行这三个会在 Service.Testing 中引入一些测试失败。即使在调试模式下运行所有​​项目的单元测试也会出现同样的问题。

我已将问题回溯到此代码。我已经添加了命令 == null 检查以使单元测试很好地失败,而不是返回愚蠢的 rhino.mocks 失败。

var command = Bus.CreateInstance<IEmailMessage>(a =>
{
    a.ToAddress = toAddresses;
    a.FromAddress = _formServiceEmailAddress;
    a.Subject = emailSubject;
    a.Body = body.ToString();
    a.IsHtmlBody = true;
});

if (command == null)
{
    throw new Exception("Email Message Failed to Create");
}

Bus.Send(command);

我没有包含测试代码,因为我已经从单元测试中删除了我的 .ExpectSend 代码,但这里仍然失败。

Bus.CreateInstance<IEmailMessage> is returning null.

Bus 不为空,我选择了所有单元测试项目并运行调试。

在写这个问题时,我可能离解决问题越来越近了。以下来自QuickWatch-ing Bus while debugging

((IBusProxyc9a2035def74446cb4f71fea5bf76c47)(Bus))
+       __interceptors  {Castle.Core.Interceptor.IInterceptor[1]}   Castle.Core.Interceptor.IInterceptor[]

“CreateInstance”是否可能已被代理并且不再工作?我没有在我的传奇中的其他任何地方使用 Bus.CreateInstance,但是这个 IEMailMessage 是唯一的接口(我们所有的服务都使用它来发送到标准的电子邮件发件人服务。

我刚刚修改了我的另一条消息以使用 Bus.CreateInstance,但这次是在一个对象上,并且遇到了同样的问题。

我对 Bus.Send 进行了同样的修改,但没有遇到同样的问题。

我现在将我的原始代码更新为

Bus.Send<IEmailMessage>(a =>
{
    a.ToAddress = toAddresses;
    a.FromAddress = _formServiceEmailAddress;
    a.Subject = emailSubject;
    a.Body = body.ToString();
    a.IsHtmlBody = true;
});

运行单元测试,现在不会对该代码抛出异常。

但是现在我的单元测试并没有达到我的预期。

InitiatedTestSaga(command)
    .ExpectPublish<FinanceUserAccessFormSubmitted>(m => CompareSubmitted(m,command) )
    .ExpectPublish<AssignedToManager>(m =>
    {
        Console.WriteLine("AssignedToManager");
        return m.FormId == command.FormId;

    })
    .ExpectSend<CreateFormSummary>(m =>
    {
        Console.WriteLine("CreateFormSummary");
        return m.FormId == command.FormId;

    })
    .ExpectSend<CreateFormHistory>(m =>
    {
        Console.WriteLine("CreateFormHistory");
        return m.FormId == command.FormId;

    })
    .ExpectSend<UpdateFormSummary>(m =>
    {
        Console.WriteLine("UpdateFormSummary");
        return m.FormId == command.FormId;

    })
    .ExpectSend<IEmailMessage>(m =>
    {
        Console.WriteLine("IEmailMessage");
        return TestCommands.ExpectedReceipientIs(m, command.FormDetails.ManagerForApproval.EmailAddress);
    })      
    .ExpectReply<CompletionMessage>(m => m.ErrorCode == (int) ResponseCode.Success)
    .When(s => s.Handle(command));

输出是

AssignedToManager
CreateFormSummary
CreateFormHistory
UpdateFormSummary
Rhino.Mocks.Exceptions.ExpectationViolationException : IBus.Send(callback method: <>c__DisplayClass2`1.<ExpectSend>b__1); Expected #1, Actual #0.

这表示 IEmailMessage 从未发送过。

使用 Bus.Send 的行为是否可能与 Bus.Send 不同?现在发送消息的顺序是否可能不同。在 saga 代码中,iemailMessage 是最后一条消息,因此应该是单元测试收到的最后一条消息。

所以稍微回溯一下。当我单独运行测试时会发生什么?这是同一单元测试的输出

AssignedToManager
CreateFormSummary
CreateFormHistory
UpdateFormSummary
IEmailMessage
ExpectedReceipientIs__["Manager@domain.com"]

所以当所有测试项目都运行时,我们仍然遇到问题。我想如果我改变期望的顺序,测试可能会在全部运行时工作,但单独它会失败。

当更改 IEmailMessage 期望的顺序时,无论我把它放在哪里(即使在发布期望之间),我都会收到此错误消息

System.InvalidCastException : Unable to cast object of type 'CreateFormSummary' to type 'IEmailMessage'.

所以现在我的问题看起来不尊重它是一个接口。我将 IEmailMessage 替换为具有相同属性的本地类

创造了这个

public class EmailMessage : IEmailMessage
{
    public string BCCAddress { get; set; }
    public string Body { get; set; }
    public string CCAddress { get; set; }
    public string FromAddress { get; set; }
    public bool IsHtmlBody { get; set; }
    public string Subject { get; set; }
    public string ToAddress { get; set; }
}

//replaced IEmailMessage with concrete
Bus.Send<EmailMessage>(a =>
{
    a.ToAddress = toAddresses;
    a.FromAddress = _formServiceEmailAddress;
    a.Subject = emailSubject;
    a.Body = body.ToString();
    a.IsHtmlBody = true;
}); 

//Run All Unit tests 
//output of this unit test
AssignedToManager
CreateFormSummary
CreateFormHistory
UpdateFormSummary
IEmailMessage
ExpectedReceipientIs__["Manager@domain.com"]

只是为了好玩,决定将我的代码还原回 Bus.CreateInstance 看看它是否仍然失败。

private void CreateAndSendEmail(string[] emailAddresses, string emailSubject, StringBuilder body)
{           
    string toAddresses = String.Join(",", emailAddresses);

    var msg = Bus.CreateInstance<IEmailMessage>(a=> 
    {
        a.ToAddress = toAddresses;
        a.FromAddress = _formServiceEmailAddress;
        a.Subject = emailSubject;
        a.Body = body.ToString();
        a.IsHtmlBody = true;
    })  ;

    if (msg == null)
    {
        throw new Exception("msg is null");
    }

    Bus.Send(msg);
}

//Run All Unit Tests
//output is this
AssignedToManager
CreateFormSummary
CreateFormHistory
UpdateFormSummary
IEmailMessage
ExpectedReceipientIs__
["Manager@wesleymission.org.au"]

什么?!它有效

我做了什么?我不确定。

我将继续发布这个问题,因为其他人可能会经历我所做的事情。 NSB/特定团队也可能会感谢您的反馈。

我不会轻易放弃

等一下,我不相信我做错了什么,让我再试试看。

  1. 关闭解决方案
  2. 删除所有Debug文件夹,
  3. 删除所有 obj 文件夹。
  4. 重建解决方案
  5. 关闭现有的测试会话
  6. 重新运行所有单元测试 -- 通过
  7. 单独运行单元测试项目 -- 通过

所以我的具体 EmailMessages 仍然存在,但我不再在任何地方提及它。所以我可以杀了它。

  1. 删除了具体的 EmailMessages
  2. 重新运行所有单元测试 -- 失败!
  3. 单独运行 -- 通过

单元测试的输出是

AssignedToManager
CreateFormSummary
CreateFormHistory
UpdateFormSummary
System.Exception : msg is null 

Saga 在两个实例中都调用了Bus.CreateInstance

解决方案

创建一个接口的实现,然后不使用它。

【问题讨论】:

  • 我所能建议的就是迁移到更新的版本。自 v2.5 以来已经修复了很多错误,我们真的不可能支持它。 2 年前发布的 3.2.8 版本是我们目前仍在修补的最早版本。
  • 感谢 Udi,我们目前正计划升级到 NSB 4.5。我会更新结果。目前正在与 Kimberley 讨论许可问题。

标签: c# unit-testing nservicebus


【解决方案1】:

解决方案

创建一个接口的实现,然后不使用它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-14
    • 2020-11-02
    • 1970-01-01
    • 1970-01-01
    • 2014-09-10
    • 1970-01-01
    • 2014-08-10
    • 2020-07-20
    相关资源
    最近更新 更多