如果从异步方法返回正确的类型,我想测试。此方法在依赖项类中使用另一个异步方法。依赖类实现此接口:
Task<string> DownloadStringAsync(string url);
我想测试的方法是:
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 };
}
}
使用xUnit和Moq进行测试成功:
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);
}
}
使用MSpec和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>();
}
这些Because
语句
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;
此行
中的NullReferenceException
失败
public async Task<T> GetData<T>(string url) where T : class , new()
{
string jsonData = await _webClientWrapper.DownloadStringAsync(url);
// ...
}
解决
在等待回复的同时,做了一些重构并voil!我创建了一个带有Establish
语句的基类,并在那里启动了模拟对象:
public class JsonWebServiceClassProviderSpecs
{
protected static JsonWebServiceClassProvider JsonWebServiceClassProvider;
protected static Mock<IWebClientWrapper> WebClientWrapperMoq;
Establish context = () =>
{
WebClientWrapperMoq = new Mock<IWebClientWrapper>();
JsonWebServiceClassProvider = new JsonWebServiceClassProvider(WebClientWrapperMoq.Object);
};
}
我更新了测试类:
[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>();
}
答案 0 :(得分:4)
这是您的规范的精简版本。没有NullReferenceException
可见。
注意:
It
不检查AwaitResult
的类型,而是检查包裹的Task.Result
Moq.It<string>.Any...
中传递Because
,这太多了。如果忽略该参数,请使用传达该事实的值。(只是一些文字,以便下面的代码块格式正确。)
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>();
}
}
答案 1 :(得分:0)
我在IoC中使用这种方法:
1)创建任务运行器界面和实现
public interface ITaskRunner
{
Task<TNewResult> Execute<TResult, TNewResult>(Func<TResult> action, Func<Task<TResult>, TNewResult> continueWith);
}
public class TaskRunner : ITaskRunner
{
public Task<TNewResult> Execute<TResult, TNewResult>(Func<TResult> action, Func<Task<TResult>, TNewResult> continueWith)
{
return Task.Factory.StartNew(action).ContinueWith(continueWith);
}
}
和用法:
public Task<JsonResult> CheckForOnline(Int64? adId)
{
ITaskRunner taskRunner = IoCFactory.Instance.TryResolve<ITaskRunner>();
return taskRunner.Execute(() => CheckForOnlineFunc(adId),
r => Json(r.Result, JsonRequestBehavior.AllowGet));
}
2)创建在同步模式下运行func的伪实现(无异步调用)
internal class FakeTaskRunner : ITaskRunner
{
public Task<TNewResult> Execute<TResult, TNewResult>(Func<TResult> action, Func<Task<TResult>, TNewResult> continueWith)
{
Task<TResult> task = new Task<TResult>(action);
try
{
task.RunSynchronously();
if (task.Exception != null)
throw task.Exception;
return task.ContinueWith(continueWith);
}
catch (Exception ex)
{
throw ((AggregateException)ex).InnerExceptions[0];
}
}
}
因此,相同的代码将在实时版本中运行async并在测试中运行async。 只需确保将IoC配置为在live和FakeTaskRunner中使用普通的TaskRunner。