NUnit testing of Async Task fails in C# - c#

I have the following class and the interface
public interface IService
{
Task<double> GetAccDetails(int personId);
}
public class Person
{
private int _personId;
private IService _service;
public Person(int personId, IService service)
{
_personId= personId;
_service = service;
}
public double Amount {get; set;}
public async void UpdateBanckingAcc()
{
Amount = await _service.GetAccDetails(_personId);
}
}
I am trying to write nunit test for it:
[Test]
public async void Test1([Values(200)]int personId)
{
const double expectedResult = 20;
var serviceMock = new Mock<IAccountService>();
//Here I tried both options:
//serviceMock.Setup(s => s.GetAccDetails(It.Is<int>(id => id == personId)))
// .ReturnsAsync(() => expectedResult);
//And:
serviceMock.Setup(s=> s.GetAccDetails(It.Is<int>(id => id == personId)))
.Returns(() => Task.FromResult<double>(personId));
var person = new Person(personId, serviceMock.Object);
person.UpdateBanckingAcc();
double res = person.Amount;
Assert.AreEqual(expectedResult, res);
}
And the test fails. For some strange reason I can not debug it.
So the issue I see here is the call :
person.UpdateBanckingAcc();
it should be
await person.UpdateBanckingAcc();
but it does not like if I use await keyword.
Please advise.
Also one more question: is there something specific in terms of nunit testing for async methods I should test, like task status testing, etc?

The problem here is that your method UpdateBankingAcc has return type void which should be returning a Task<T> or Task, so you need to change the signatures of it to reutrn a Task like:
public async Task UpdateBanckingAcc()
{
Amount = await _service.GetAccDetails(_personId);
}
and now you would need to change your test code to be:
await person.UpdateBanckingAcc();
double res = person.Amount;
Assert.AreEqual(expectedResult, res);
you should never return void from an async method, unless it is UI controls events, you can read about the best practices of using async and await at following :
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
http://www.tonicodes.net/blog/why-you-should-almost-never-write-void-asynchronous-methods/
async/await - when to return a Task vs void?

There is a simple rule: async void is used for fire-and-forget behavior and only for this. If you need async method, change it's return type to the Task, as #EhsanSajjad said. This corresponds to the unit tests too:
public async Task UpdateBanckingAcc()
public async Task Test1([Values(200)]int personId)

Related

How to test method include async method in Unity

I want to test method using async method.
But, when i run test, UnityEditor is blocking.
I think, this problem is because async method.
i don't know how to solve this problem.
Unity version : Unity 2020.3.33f1
public async static Task<string> GetAsync() {
// Do something
HttpResponseMessage response = await request.GetAsync(..);
string responseData = await response.Content.ReadAsStringAsync();
return response Data
}
...
public string GetData() {
Task<string> res = GetAsync();
return res.Result;
}
////////////////// Test Code //////////////
[Test]
public void Test_GetData() {
...
string res = GetData()
...
}
Without testing it, you need to use async/await in all places.
When using async/await, as you have done Task for returning string type, but for void method, you just use Task.
let's say
public static async Task<string> GetAsync()
{
return await Task.FromResult("test");
}
and your test should be like this:
[Test]
public async Task Test_GetAsync()
{
var result = await GetAsync();
Assert.AreEqual("test", result);
}
if we take your code so
public async static Task<string> GetAsync() {
// what ever
.....
return responseData
}
the test will be:
[Test]
public async Task Test_GetData() {
...
string res = await GetData()
...
}
If your test project is an older .net framework, you would level it up to a newer version as this answer suggests. Otherwise, if you are locked and not able to upgrade, you can do something which I am not suggesting as this answer mentions.
[Test]
public void Test_GetData()
{
var result = GetAsync().GetAwaiter();
Assert.AreEqual("test", result.GetResult());
}

How to compose a unit test if a task is followed by task.start()?

In program, we have:
var task1 = some task;
task1.start()
If mock the task result in unit test, the result is returned by mock mechanism immediately before calling the start(), and raise "Start may not be called on a task that has completed" exception.
How to address that issue and compose a valid unit test?
Paste a simplified sample code here for clarity, which produces the above exception:
namespace TestTaskStart
{
public class TestMethods : ITestMethods
{
public async Task<int> AlwaysReturnOne(int number)
{
return await Task.FromResult(1);
}
}
}
namespace TestTaskStart {
public class TestInvoker
{
private ITestMethods testMethods;
public TestInvoker(ITestMethods testMethods)
{
this.testMethods = testMethods;
}
public async Task<int> GetANumberWrapperTask(int number)
{
// just an exmple of one tasks to be called
var task = this.testMethods.AlwaysReturnOne(number);
task.Start();
Task.WaitAll(task);
return task.Result;
}
}
}
namespace TestTaskStart {
[TestClass]
public class UnitTests
{
ITestMethods numberGetter;
TestInvoker testInvoker;
[TestInitialize]
public void Setup()
{
this.numberGetter = Substitute.For<ITestMethods>();
this.testInvoker = new TestInvoker(this.numberGetter);
}
[TestMethod]
public void TestGetANumberWrapper()
{
this.MockAlwaysReturnOneResult();
var result = testInvoker.GetANumberWrapperTask(5).Result;
}
private void MockAlwaysReturnOneResult()
{
this.numberGetter.AlwaysReturnOne(Arg.Any<int>()).Returns(1);
}
}
}
The Task.Start method can only be called on "cold" tasks, in other words on tasks that have not been started yet. Such tasks can only be created with the Task constructor. Tasks created by invoking asynchronous methods implemented with the async keyword are "hot", in other words they are already started upon creation. From the docs:
Exceptions
InvalidOperationException
The Task is not in a valid state to be started. It may have already been started, executed, or canceled, or it may have been created in a manner that doesn't support direct scheduling.
This is also a good reading: A Tour of Task, Part 10: Promise Tasks

How do you Mock LUIS during Unit Tests?

I have been searching for quick samples, tutorials, etc. about how to mock LUIS using Moq or what not in unit tests on Microsoft Botframework, but found none. Some in the net are 2 years old and the link to Github are unavailable. I hope somebody can help me here. Some details to the bot:
Language: C# 4.4
Framework: .NET 4.6
Botframework SDK: 3.x
Example code:
public async Task Process(IDialogContext context, LuisResult luisResult)
{
string emoji = luisResult.Query;
if (emoji.Contains(":)") || emoji.Contains(": )"))
{
await context.PostAsync(":)");
}
}
LuisResult is not an Interface making it harder to use mocking Frameworks in general.
You can create your own interface and an implementation that forwards all calls so that you can then mock that interface.
internal interface ILuisResult
{
string Query { get; }
}
internal class LuisResultAdapter : ILuisResult
{
private readonly LuisResult _luisResult;
public LuisResultAdapter(LuisResult luisResult)
{
_luisResult = luisResult;
}
public string Query => _luisResult.Query;
}
Alternatively you can refactor the Process so that it no longer directly depends on LuisResult by wrapping just the call with a function and pass in a lambda for testing.
public void CallOptions(IDialogContext context, LuisResult luisResult)
{
Process(context, () => luisResult.Query).Wait();
Process(context, () => "testData").Wait();
}
public async Task Process(IDialogContext context, Func<string> query)
{
string emoji = query();
if (emoji.Contains(":)") || emoji.Contains(": )"))
{
await context.PostAsync(":)");
}
}
EDIT: As per request in the comments here is a more detailed example for testing.
[TestFixture]
public class ProcessFixture
{
private ILuisResult _luisResult;
private BotHost _tested;
private IDialogContext _dialogContext;
private string _posted = null;
[SetUp]
public void SetUp()
{
_posted = null;
_luisResult = Rhino.Mocks.MockRepository.GenerateMock<ILuisResult>();
_dialogContext = Rhino.Mocks.MockRepository.GenerateMock<IDialogContext>();
_dialogContext
.Stub(x => x.PostAsync(Arg<string>.Is.Anything))
.Do((Func<string, Task>) (s =>
{
_posted = s;
return Task.Factory.StartNew(() => { });
}));
_tested = new BotHost(); //this is a made up class so I can call a method on it
}
[TestCase("", ExpectedResult = null)]
[TestCase(":)", ExpectedResult = ":)")]
[TestCase(": )", ExpectedResult = ":)")]
public string ProcessCleansUpInputs(string input)
{
_luisResult.Stub(x => x.Query).Return(input);
_tested.Process(_dialogContext, _luisResult).Wait();
return _posted;
}
}
packages in use for this:
"NUnit" version="3.11.0" so that i have an xunit framework
"NUnit3TestAdapter" version="3.11.2" so that I can run the tests in my vs ide using Test->Windows->Test Explorer
"RhinoMocks" version="3.6.1" so that i can create stubs from interfaces

Moq: setup a generic method with mocked parameters

I've been trying to write a few tests in NUnit for my generic methods, without success. I hope I can make my situation clear, since I had to heavily paraphrase my code.
DoBusinessLogic() is the method I want to test. It calls two other methods from the base class:
public class MySvc : BaseSvc, IMySvc
{
private readonly IMyRepo _repo;
private readonly IMyConnectorClass _connector
public MySvc(IMyRepo repo) : base(repo)
{
_repo = repo;
_connector = _repo.GetConnector();
}
public async Task DoBusinessLogic(int id1, int id2){
bool doesFirstObjectExist = await CheckMainObjExists<Foo>(id1, _connector);
await CheckSubObjExists<Bar>(id2, _connector);
// further business logic
}
}
Those methods, in turn, call the same repository method, but have different logic after it:
public abstract class BaseSvc : IBaseSvc
{
private readonly IBaseRepo _repo
protected BaseSvc(IBaseRepo repo)
{
_repo = repo;
}
protected async Task<bool> CheckMainObjExists<T>(int? id, IMyConnectorClass connector)
{
return await _repo.GetObjectByIdAsync<T>(Id, connector) != null;
}
protected async Task CheckSubObjExists<T>(int? id, IMyConnectorClass connector)
{
if (await _repo.GetObjectByIdAsync<T>(Id, connector) == null)
{ throw new Exception("Object not found!"); }
}
}
Next, I want to write unit a test for DoBusinessLogic() in the MySvc class. Unfortunately, it seems I can't mock the responses from the repository.
[TestFixture]
public class MySvcTests
{
private MySvc _svc;
private Mock<IMyRepo> _repoMock;
private Mock<IMyConnectorClass> _connectorMock;
[SetUp]
public void SetUp()
{
_repoMock = new Mock<IMyRepo>() {};
_connectorMock = new Mock<IMyConnectorClass>();
_repo.SetUp(r => r.GetConnector()).Return(_connectorMock.Object);
_svc = new MySvc(_repoMock);
}
/*
My intent in this test, is to make CheckMainObjExists() pass,
but make CheckSubObjExist() fail.
*/
[Test]
public async Task DoBusinessLogic_If2ndObjectNotExist_ThrowException()
{
// This should return an object
_repoMock.Setup(r => r.GetObjectByIdAsync<Foo>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync(new Foo());
// This should return null
_repoMock.Setup(r => r.GetObjectByIdAsync<Bar>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync((Bar) null);
Assert.Throws<Exception>(await _svc.DoBusinessLogic());
}
}
However, when I run the test, both methods that I set up for my mock repo return null, whereas I expect a "true" from the first.
I do not know where the problem is situated, but I have my suspicions:
Is it possible to setup a method, using a mocked object as a parameter? In this case, is it possible to use _connectorMock.Object as a setup parameter?
Is it possible to setup the same generic method multiple times, but for a different type each time? It's first setup for Foo, then for Bar.
I just tested this code and it runs as expected. Now I had to make a lot of assumptions just to get the code to compile and run which would mean that my test of your code is flawed as I may have fixed something that was omitted in your example.
I made no changes to your test setup code, which worked.
[TestClass]
public class MySvcTests {
[TestMethod]
[ExpectedException(typeof(Exception))]
public async Task DoBusinessLogic_If2ndObjectNotExist_ThrowException() {
var _repoMock = new Mock<IMyRepo>() { };
var _connectorMock = new Mock<IMyConnectorClass>();
_repoMock.Setup(r => r.GetConnector()).Returns(_connectorMock.Object);
var _svc = new MySvc(_repoMock.Object);
// This should return an object
_repoMock.Setup(r => r.GetObjectByIdAsync<Foo>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync(new Foo());
// This should return null
_repoMock.Setup(r => r.GetObjectByIdAsync<Bar>(It.IsAny<int>(), _connectorMock.Object))
.ReturnsAsync((Bar)null);
await _svc.DoBusinessLogic(0, 0);
}
}

NSubstitute returning NullReferenceException when Using ForPartsOf and mocking an Async Method

I have two methods:
public class MyClass
{
public virtual async Task MethodOne(MyModel myModel)
{
await Task.Delay(1);
throw new Exception("Test");
}
public async Task MethodTwo()
{
MyModel myModel = new MyModel();
await MethodOne(myModel);
}
}
It doesn't matter what MyModel is, but it does matter that it's a parameter.
And the test:
[Fact]
public async Task Test6()
{
// Arrange
MyClass myClass = Substitute.ForPartsOf<MyClass>();
myClass.When(async a => await a.MethodOne(Arg.Any<MyModel>())).DoNotCallBase();
// Act
await myClass.MethodTwo();
// Assert
}
The test gives a NullReferenceException when the line:
myClass.When(async a => await a.MethodOne(Arg.Any<MyModel>())).DoNotCallBase();
My guess is that it is, in some way, trying to resolve MyModel; however, the same test works, when performed on a synchronous method, or one without a complex parameter.
Can anyone tell me why this error occurs in this way?
Do not await the setup
overridden method needs to return a Task to allow async to flow to completion when invoked in test.
That means the setup needs to be rewritten
[Fact]
public async Task Test6() {
// Arrange
var myClass = Substitute.ForPartsOf<MyClass>();
myClass.MethodOne(Arg.Any<MyModel>()).Returns(Task.FromResult((object)null));
// Act
await myClass.MethodTwo();
// Assert
//...
}

Categories

Resources