How do you Mock LUIS during Unit Tests? - c#

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

Related

How to parallelize all the tests in an xunit collection fixture

The Problem
I have a collection fixture in xunit test that lets me group all my tests together to allow for 1) a docker db setup and 2) each test to have their own service collection for activities.
The problem is that the since the tests are all in a collection fixture, the tests classes don't run in parallel. I've tried a few different things in their parallelization docs with no luck. They still run pretty fast but it seems like this would be a big bump in perf that should be doable. Any thoughts here?
The Code
i have a test collection fixture like this
[CollectionDefinition(nameof(TestFixture))]
public class TestFixtureCollection : ICollectionFixture<TestFixture> {}
public class TestFixture : IAsyncLifetime
{
public static IServiceScopeFactory BaseScopeFactory;
public async Task InitializeAsync()
{
// docker db setup
builder.ConfigureServices();
var services = builder.Services;
var provider = services.BuildServiceProvider();
BaseScopeFactory = provider.GetService<IServiceScopeFactory>();
}
public Task DisposeAsync()
{
return Task.CompletedTask;
}
}
public static class ServiceCollectionServiceExtensions
{
public static IServiceCollection ReplaceServiceWithSingletonMock<TService>(this IServiceCollection services)
where TService : class
{
services.RemoveAll(typeof(TService));
services.AddScoped<TService>(_ => Mock.Of<TService>());
return services;
}
}
testing service scope
public class TestingServiceScope
{
private readonly IServiceScope _scope;
public TestingServiceScope()
{
_scope = BaseScopeFactory.CreateScope();
}
public TScopedService GetService<TScopedService>()
{
var service = _scope.ServiceProvider.GetService<TScopedService>();
return service;
}
public async Task<T> ExecuteScopeAsync<T>(Func<IServiceProvider, Task<T>> action)
=> await action(_scope.ServiceProvider);
public Task<T> ExecuteDbContextAsync<T>(Func<CoreDomainDbContext, Task<T>> action)
=> ExecuteScopeAsync(sp => action(sp.GetService<CoreDomainDbContext>()));
public Task<int> InsertAsync<T>(params T[] entities) where T : class
{
return ExecuteDbContextAsync(db =>
{
foreach (var entity in entities)
{
db.Set<T>().Add(entity);
}
return db.SaveChangesAsync();
});
}
}
example test:
[Collection(nameof(TestFixture))]
public class AddRecipeCommandTests
{
[Fact]
public async Task can_add_new_recipe_to_db()
{
// Arrange
var testingServiceScope = new TestingServiceScope();
var fakeRecipeOne = new FakeRecipeForCreationDto().Generate();
// Act
var command = new AddRecipe.Command(fakeRecipeOne);
var recipeReturned = await testingServiceScope.SendAsync(command);
var recipeCreated = await testingServiceScope.ExecuteDbContextAsync(db => db.Recipes
.FirstOrDefaultAsync(r => r.Id == recipeReturned.Id));
// Assert
recipeReturned.Title.Should().Be(fakeRecipeOne.Title);
recipeReturned.Visibility.Should().Be(fakeRecipeOne.Visibility);
recipeReturned.Directions.Should().Be(fakeRecipeOne.Directions);
recipeReturned.Rating.Should().Be(fakeRecipeOne.Rating);
recipeReturned.DateOfOrigin.Should().Be(fakeRecipeOne.DateOfOrigin);
recipeReturned.HaveMadeItMyself.Should().Be(fakeRecipeOne.HaveMadeItMyself);
recipeCreated.Title.Should().Be(fakeRecipeOne.Title);
recipeCreated.Visibility.Should().Be(fakeRecipeOne.Visibility);
recipeCreated.Directions.Should().Be(fakeRecipeOne.Directions);
recipeCreated.Rating.Should().Be(fakeRecipeOne.Rating);
recipeCreated.DateOfOrigin.Should().Be(fakeRecipeOne.DateOfOrigin);
recipeCreated.HaveMadeItMyself.Should().Be(fakeRecipeOne.HaveMadeItMyself);
}
}

System.NullReferenceException when unit testing

I am new to unit testing,
I am working on a minimal API project. and I am testing an endpoint with xunit, moq
endpoint class - ParticipantsGetAll.cs
public class ParticipantsGetAll : IEndpoint<IResult, GetAllParticipantsRequest>
{
const string uri = "api/participants-all";
private ParticipantService? participantService;
public void AddRoute(IEndpointRouteBuilder app)
{
app.MapPost(uri, async ( ParticipantService participantService, [FromBody] GetAllParticipantsRequest query) =>
{
this.participantService = participantService;
return await HandleAsync(query);
})
.Produces<List<ParticipantSummaryModel>>()
.WithTags("Participants")
.WithName("GetAllParticipants");
}
public async Task<IResult> HandleAsync( GetAllParticipantsRequest query)
{
var participants = await participantService!.ListAllAsync(query);
return Results.Ok(participants);
}
I tried to write a unit test test above endpoint class.
CreateParticipantApiTest.cs
[Fact]
public async void ListAllAsyncShouldReturn200Status()
{
var query = new GetAllParticipantsRequest()
{
Name= "",
KeyCordinator="",
RelatedConnection="",
Statuses = null
};
var participant = ParticipantMockData.NewParticipantModel(); // a static class
var sut = new ParticipantsGetAll();
var result = (OkObjectResult)await sut.HandleAsync(query);
result.StatusCode.Should().Be(200);
}
I got below error
Message: 
System.NullReferenceException : Object reference not set to an instance of an object.
Stack Trace: 
ParticipantsGetAll.HandleAsync(GetAllParticipantsRequest query) line 36
CreateParticipantApiTest.ListAllAsyncShouldReturn200Status() line 67
I have no idea why the object tis null.
Please anyone help me to find the problem.
I am facing this issue for a long time
Thanks
I have no idea why the object tis null.
That is pretty clear, because in your unit test you invoke HandleAsync directly, so the setup which you have moved into MapPost does not happen (compiler was trying to help but was shut down with null-forgiving operator in participantService!.ListAllAsync(query)). Also I'm pretty sure this way of using handlers can lead to some concurrency problems (if ParticipantService is a scoped service). Move ParticipantService participantService to HandleAsync. Something along this lines (not tested):
public class ParticipantsGetAll : IEndpoint<IResult, GetAllParticipantsRequest>
{
const string uri = "api/participants-all";
public void AddRoute(IEndpointRouteBuilder app)
{
app.MapPost(uri, HandleAsync)
.Produces<List<ParticipantSummaryModel>>()
.WithTags("Participants")
.WithName("GetAllParticipants");
}
public async Task<IResult> HandleAsync(ParticipantService participantService, [FromBody] GetAllParticipantsRequest query)
{
var participants = await participantService.ListAllAsync(query);
return Results.Ok(participants);
}
}
And modify the test accrodingly.

Unit testing a c# Method using Moq

I'm new to unit testing and the language c sharp and I'm trying to unit test by mocking ISessionStorageService to test the HasGlobaladminaccess function but my setup does not seem to work as expected.
public class Context
{
public asyncTask<bool>HasGlobalAdminAccessAsync(Blazored.SessionStorage.ISessionStorageService sessionStorage)
{
return await HasAccessAsync(sessionStorage, Config.Constants.HAS_GLOBALE_ADMIN_ACCESS);
}
private async Task<bool> HasAccessAsync(Blazored.SessionStorage.ISessionStorageService sessionStorage, string sessionStorageKey)
{
ILogger _logger = Log.ForContext<ContextHelpers>();
try
{
_logger.Debug("HasAccessAsync({sessionStorageKey})", sessionStorageKey);
var access = await sessionStorage.GetItemAsync<bool>(sessionStorageKey);
_logger.Debug("HasAccessAsync({sessionStorageKey})::access={access}", sessionStorageKey, access);
return access;
}
catch (Exception ex)
{
_logger.Debug("HasAccessAsync({sessionStorageKey})::exception={ex}", sessionStorageKey, ex.Message);
return false;
}
}
}
My test method
private readonly Mock<ISessionStorageService> MockStorage = new Mock<ISessionStorageService>();
[Fact()]
public async Task HasGlobalAdminAccessAsyncTest()
{
string guid = System.Guid.NewGuid().ToString();
Context context = new Context();
MockStorage.Setup(foo => foo.GetItemAsync<bool>(guid)).ReturnsAsync(true);
var person = await context.HasGlobalAdminAccessAsync(MockStorage.Object);
Assert.True(person , guid);
}
Either use the same value as what's in Config.Constants.HAS_GLOBALE_ADMIN_ACCESS for the GUID value in your test
Or use
foo => foo.GetItemAsync<bool>(It.IsAny<Guid>())
In your Setup call.
As it's written, you have your test set to only accept the exact GUID that is returned from Guid.NewGuid() in your test, so it's not going to work in general.

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);
}
}

NUnit testing of Async Task fails in 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)

Categories

Resources