为什么要测试与MSpec /起订量这种异步方法时,我得到一个NullReferenceException? [英] Why do I get a NullReferenceException when testing this async method with MSpec/Moq?

查看:193
本文介绍了为什么要测试与MSpec /起订量这种异步方法时,我得到一个NullReferenceException?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想测试,如果正确的类型从一个异步方法返回。此方法使用的依赖类另一个异步方法。依赖类实现这个接口:

 任务<串GT; DownloadStringAsync(字符串URL);

我要测试的方法是这样的:

 公共异步任务< T>的GetData< T>(字符串URL)其中T:类,新的()
{
    VAR jsonData =等待_webClientWrapper.DownloadStringAsync(URL);    如果(string.IsNullOrEmpty(jsonData))
        返回新T();    尝试
    {
        返回等待JsonConvert.DeserializeObjectAsync< T>(jsonData);
    }
    赶上(JsonException内)
    {
        抛出新JsonConvertException(错误转换成JSON字符串,内部){JsonString = jsonData};
    }
}

用的xUnit和起订量测试成功:

 公共类睾丸
{
    私人常量字符串ValidJson ={'诺姆':'罗热','Idade':'51'};
    受保护的静态JsonWebServiceClassProvider JsonWebServiceClassProvider;
    私有静态模拟< IWebClientWrapper> _webClientWrapperMoq;
    私有静态FakeClassFromJson _resultClass;    [事实]
    公共异步静态无效When_calling_GetData_it_should_return_a_class_of_same_type()
    {
        _webClientWrapperMoq =新的模拟< IWebClientWrapper>();
        _webClientWrapperMoq
            .Setup(W => w.DownloadStringAsync(Moq.It.IsAny<串GT;()))
            .Returns(Task.FromResult(ValidJson));        JsonWebServiceClassProvider =新JsonWebServiceClassProvider(_webClientWrapperMoq.Object);        _resultClass =等待JsonWebServiceClassProvider
            .GetData&所述; FakeClassFromJson>(Moq.It.IsAny&所述;串GT;());        Assert.IsType< FakeClassFromJson>(_ resultClass);
    }
}

与MSpec和起订量测试:

  [主题(JsonWebServiceClassProvider)]
公共类When_calling_GetData_with_a_valid_Json_Service_Url
{
    私人常量字符串ValidJson ={'诺姆':'罗热','Idade':'51'};
    受保护的静态JsonWebServiceClassProvider JsonWebServiceClassProvider;
    受保护的静态模拟< IWebClientWrapper> WebClientWrapperMoq;
    受保护的静态FakeClassFromJson ResultClass;    建立上下文=()=>
    {
        WebClientWrapperMoq =新的模拟< IWebClientWrapper>();
        WebClientWrapperMoq
            .Setup(W => w.DownloadStringAsync(Moq.It.IsAny<串GT;()))
            .Returns(Task.FromResult(ValidJson));        JsonWebServiceClassProvider =新JsonWebServiceClassProvider(WebClientWrapperMoq.Object);
    };    由于=()的= GT; ResultClass = JsonWebServiceClassProvider
        .GetData< FakeClassFromJson>(Moq.It.IsAny<串GT;())
        。等待();    它should_return_a_class_of_same_type =()=> ResultClass.ShouldBeOfType< FakeClassFromJson>();
}

它也不能与这些因为语句

 由于=()=>将JsonWebServiceClassProvider
    .GetData< FakeClassFromJson>(Moq.It.IsAny<串GT;())
    .ContinueWith(任务=> ResultClass = task.Result)
    。等待();由于=()的= GT; ResultClass = JsonWebServiceClassProvider
    .GetData< FakeClassFromJson>(Moq.It.IsAny<串GT;())
    。结果;

这失败了的NullReferenceException 的行

 公共异步任务< T>的GetData< T>(字符串URL)其中T:类,新的()
{
    字符串jsonData =等待_webClientWrapper.DownloadStringAsync(URL);
    // ...
}

解决

在等待回应,做了一些重构瞧!我创建了一个基类与制定语句,并启动了模拟对象有:

 公共类JsonWebServiceClassProviderSpecs
{
    受保护的静态JsonWebServiceClassProvider JsonWebServiceClassProvider;
    受保护的静态模拟< IWebClientWrapper> WebClientWrapperMoq;    建立上下文=()=>
    {
        WebClientWrapperMoq =新的模拟< IWebClientWrapper>();
        JsonWebServiceClassProvider =新JsonWebServiceClassProvider(WebClientWrapperMoq.Object);
    };
}

和我更新了测试类:

  [主题(JsonWebServiceClassProvider)]
公共类When_ask_data_with_a_valid_Json_Service_Url:JsonWebServiceClassProviderSpecs
{
    私人常量字符串ValidJson ={'诺姆':'罗热','Idade':'51'};
    受保护的静态FakeClassFromJson ResultClass;    建立上下文=()=>
    {
        WebClientWrapperMoq
            .Setup(W => w.DownloadStringAsync(Moq.It.IsAny<串GT;()))
            .Returns(Task.FromResult(ValidJson));
    };    由于=()的= GT; ResultClass = JsonWebServiceClassProvider
        .GetData< FakeClassFromJson>(Moq.It.IsAny<串GT;())
        。等待();    它should_return_a_class_of_same_type =()=> ResultClass.ShouldBeOfType< FakeClassFromJson>();
}


解决方案

这是您的规范,工程的精简版。否的NullReferenceException 待观察。
注:


  • 不检查的 AwaitResult 的类型,而是得到包裹 Task.Result

  • 我没有通过 Moq.It<串GT;。任何... 因为,这是太吵了。如果该参数被忽略,使用这一事实进行通信的价值。

(只是一些文本,这样下面的code座格式正确无误。)

 使用System.Diagnostics程序;
使用System.Threading.Tasks;使用Machine.Specifications;使用起订量;使用YourApp;使用它= Machine.Specifications.It;命名YourApp
{
  Foo类
  {
  }  公共接口IWebClientWrapper
  {
    任务<串GT; DownloadStringAsync(字符串URL);
  }  公共类JsonWebServiceClassProvider
  {
    只读IWebClientWrapper _webClientWrapper;    公共JsonWebServiceClassProvider(IWebClientWrapper webClientWrapper)
    {
      _webClientWrapper = webClientWrapper;
    }    公共异步任务< T>的GetData< T>(字符串URL)其中T:类,新的()
    {
      字符串jsonData =等待_webClientWrapper.DownloadStringAsync(URL);
      Debug.Assert的(jsonData!= NULL);
      返回新T();
    }
  }
}命名空间规格
{
  公共类When_calling_GetData_with_a_valid_Json_Service_Url
  {
    常量字符串ValidJson ={'诺姆':'罗热','Idade':'51'};
    静态JsonWebServiceClassProvider JsonWebServiceClassProvider;
    静态模拟< IWebClientWrapper>包装;
    静态AwaitResult<富>结果;    建立上下文=()=>
    {
      包装=新的模拟< IWebClientWrapper>();
      Wrapper.Setup(W => w.DownloadStringAsync(Moq.It.IsAny<串GT;()))
             .Returns(Task.FromResult(ValidJson));      JsonWebServiceClassProvider =新JsonWebServiceClassProvider(Wrapper.Object);
    };    由于=()的= GT;结果= JsonWebServiceClassProvider.GetData<富>(忽略),等待()。    它should_return_a_class_of_same_type =()=> Result.AsTask.Result.ShouldBeOfType<富>();
  }
}

I want test if the correct type is returned from an async method. This method uses another async method in a dependency class. The dependency class implements this interface:

Task<string> DownloadStringAsync(string url);

The method I want to test is this:

public async Task<T> GetData<T>(string url) where T : class , new()
{
    var jsonData = await _webClientWrapper.DownloadStringAsync(url);

    if (string.IsNullOrEmpty(jsonData))
        return new T();

    try
    {
        return await JsonConvert.DeserializeObjectAsync<T>(jsonData);
    }
    catch (JsonException inner)
    {
        throw new JsonConvertException("Error converting Json string", inner) { JsonString = jsonData };
    }
}

Testing with xUnit and Moq succeeds:

public class Testes
{
    private const string ValidJson = "{'Nome':'Rogerio','Idade':'51'}";
    protected static JsonWebServiceClassProvider JsonWebServiceClassProvider;
    private static Mock<IWebClientWrapper> _webClientWrapperMoq;
    private static FakeClassFromJson _resultClass;

    [Fact]
    public async static void When_calling_GetData_it_should_return_a_class_of_same_type()
    {
        _webClientWrapperMoq = new Mock<IWebClientWrapper>();
        _webClientWrapperMoq
            .Setup(w => w.DownloadStringAsync(Moq.It.IsAny<string>()))
            .Returns(Task.FromResult(ValidJson));

        JsonWebServiceClassProvider = new JsonWebServiceClassProvider(_webClientWrapperMoq.Object);

        _resultClass = await JsonWebServiceClassProvider
            .GetData<FakeClassFromJson>(Moq.It.IsAny<string>());

        Assert.IsType<FakeClassFromJson>(_resultClass);
    }
}

Testing with MSpec and Moq:

[Subject("JsonWebServiceClassProvider")]
public class When_calling_GetData_with_a_valid_Json_Service_Url
{
    private const string ValidJson = "{'Nome':'Rogerio','Idade':'51'}";
    protected static JsonWebServiceClassProvider JsonWebServiceClassProvider;
    protected static Mock<IWebClientWrapper> WebClientWrapperMoq;
    protected static FakeClassFromJson ResultClass;

    Establish context = () =>
    {
        WebClientWrapperMoq = new Mock<IWebClientWrapper>();
        WebClientWrapperMoq
            .Setup(w => w.DownloadStringAsync(Moq.It.IsAny<string>()))
            .Returns(Task.FromResult(ValidJson));

        JsonWebServiceClassProvider = new JsonWebServiceClassProvider(WebClientWrapperMoq.Object);
    };

    Because of = () => ResultClass = JsonWebServiceClassProvider
        .GetData<FakeClassFromJson>(Moq.It.IsAny<string>())
        .Await();

    It should_return_a_class_of_same_type = () => ResultClass.ShouldBeOfType<FakeClassFromJson>();
}

It also fails with these Because statements

Because of = () => JsonWebServiceClassProvider
    .GetData<FakeClassFromJson>(Moq.It.IsAny<string>())
    .ContinueWith(task => ResultClass = task.Result)
    .Wait();

Because of = () => ResultClass = JsonWebServiceClassProvider
    .GetData<FakeClassFromJson>(Moq.It.IsAny<string>())
    .Result;

This fails with a NullReferenceException in the line

public async Task<T> GetData<T>(string url) where T : class , new()
{
    string jsonData = await _webClientWrapper.DownloadStringAsync(url);
    // ...
}

SOLVED

While waiting for a response, did some refactoring and voilà! I created a base class with an Establish statement and initiated the mock object there:

public class JsonWebServiceClassProviderSpecs
{
    protected static JsonWebServiceClassProvider JsonWebServiceClassProvider;
    protected static Mock<IWebClientWrapper> WebClientWrapperMoq; 

    Establish context = () =>
    {
        WebClientWrapperMoq = new Mock<IWebClientWrapper>();
        JsonWebServiceClassProvider = new JsonWebServiceClassProvider(WebClientWrapperMoq.Object);
    };
}

And I updated the test class:

[Subject("JsonWebServiceClassProvider")]
public class When_ask_data_with_a_valid_Json_Service_Url : JsonWebServiceClassProviderSpecs
{
    private const string ValidJson = "{'Nome':'Rogerio','Idade':'51'}";
    protected static FakeClassFromJson ResultClass;

    Establish context = () =>
    {
        WebClientWrapperMoq
            .Setup(w => w.DownloadStringAsync(Moq.It.IsAny<string>()))
            .Returns(Task.FromResult(ValidJson));
    };

    Because of = () => ResultClass = JsonWebServiceClassProvider
        .GetData<FakeClassFromJson>(Moq.It.IsAny<string>())
        .Await();

    It should_return_a_class_of_same_type = () => ResultClass.ShouldBeOfType<FakeClassFromJson>();
}

解决方案

This is a slimmed-down version of your spec that works. No NullReferenceException to be seen. Note:

  • The It doesn't check the type of the AwaitResult but rather gets the wrapped Task.Result
  • I don't pass Moq.It<string>.Any... in the Because, that's too much noise. If the parameter is ignored, use a value that communicates that fact.

(Just some text such that the code block below is formatted correctly.)

using System.Diagnostics;
using System.Threading.Tasks;

using Machine.Specifications;

using Moq;

using YourApp;

using It = Machine.Specifications.It;

namespace YourApp
{
  class Foo
  {
  }

  public interface IWebClientWrapper
  {
    Task<string> DownloadStringAsync(string url);
  }

  public class JsonWebServiceClassProvider
  {
    readonly IWebClientWrapper _webClientWrapper;

    public JsonWebServiceClassProvider(IWebClientWrapper webClientWrapper)
    {
      _webClientWrapper = webClientWrapper;
    }

    public async Task<T> GetData<T>(string url) where T : class, new()
    {
      string jsonData = await _webClientWrapper.DownloadStringAsync(url);
      Debug.Assert(jsonData != null);
      return new T();
    }
  }
}

namespace Specs
{
  public class When_calling_GetData_with_a_valid_Json_Service_Url
  {
    const string ValidJson = "{'Nome':'Rogerio','Idade':'51'}";
    static JsonWebServiceClassProvider JsonWebServiceClassProvider;
    static Mock<IWebClientWrapper> Wrapper;
    static AwaitResult<Foo> Result;

    Establish context = () =>
    {
      Wrapper = new Mock<IWebClientWrapper>();
      Wrapper.Setup(w => w.DownloadStringAsync(Moq.It.IsAny<string>()))
             .Returns(Task.FromResult(ValidJson));

      JsonWebServiceClassProvider = new JsonWebServiceClassProvider(Wrapper.Object);
    };

    Because of = () => Result = JsonWebServiceClassProvider.GetData<Foo>("ignored").Await();

    It should_return_a_class_of_same_type = () => Result.AsTask.Result.ShouldBeOfType<Foo>();
  }
}

这篇关于为什么要测试与MSpec /起订量这种异步方法时,我得到一个NullReferenceException?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆