为什么要测试与MSpec /起订量这种异步方法时,我得到一个NullReferenceException? [英] Why do I get a NullReferenceException when testing this async method with MSpec/Moq?
问题描述
我想测试,如果正确的类型从一个异步方法返回。此方法使用的依赖类另一个异步方法。依赖类实现这个接口:
任务<串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 theAwaitResult
but rather gets the wrappedTask.Result
- I don't pass
Moq.It<string>.Any...
in theBecause
, 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屋!