ServiceBusTrigger Unit testing with XUNIT - c#

I have an Azure Function as below. In the following code, I'm adding a product to the database using Repository & EF Core. Is there any way we can test it using Xunit? Currently, I'm testing this using Azure Service Bus Explorer.
[FunctionName("SaveProductData")]
public void Run([ServiceBusTrigger("mytopicname", Connection = "ServiceBusConnectionString")]
ProductItemUpdate message, ILogger log)
{
var product = new Domain.Entities.Product()
{
... prop init goes here.....
};
log.LogInformation($"Processed Product - Sain: {message.ProductId}");
_productRepository.Add(product);
}

To elaborate on scottdavidwalker's comment with a full example:
public class SaveProductDataTests
{
private readonly Mock<IProductRepository> _mockProductRepository = new Mock<IProductRepository>();
private readonly Mock<ILogger> _mockLogger = new Mock<ILogger>();
private readonly SaveProductData _saveProductData;
public SaveProductDataTests()
{
_saveProductData = new SaveProductData(_mockProductRepository.Object);
}
[Fact]
public void Given_When_Then()
{
// arrange
var productItemUpdate = new ProductItemUpdate();
// act
_saveProductData.Run(productItemUpdate, _mockLogger.Object);
// assert
_mockProductRepository.Verify(x => x.Add(It.Is<Product>(p => p.SomeProperty == "xyz")));
}
}
You need to create an instance of the class you are testing and mock the dependencies.
The Azure function is essentially a method (.Run()) inside the class which you can call on the instance.
In your unit test, you create the data to trigger the method and then make assertions on your mocks for what you expect to happen when the code runs for real.

Related

More than twenty 'IServiceProvider'. Unit Test

I have this error message :
An error was generated for warning 'Microsoft.EntityFrameworkCore.Infrastructure.ManyServiceProvidersCreatedWarning': More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. This is commonly caused by injection of a new singleton service instance into every DbContext instance. For example, calling 'UseLoggerFactory' passing in a new instance each time--see https://go.microsoft.com/fwlink/?linkid=869049 for more details. This may lead to performance issues, consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. This exception can be suppressed or logged by passing event ID 'CoreEventId.ManyServiceProvidersCreatedWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.
When I run all Unit Test together
Setup
private readonly DbContextOptions<ApplicationDbContext> _contextOptions;
private readonly DbContextOptions<ApplicationDbContext> _inMemoryContextOptions;
public TestConstructor()
{
// Test for real database READ
_contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer(_connectionString)
.Options;
// Test InMemory CREATE UPDATE DELETE
_inMemoryContextOptions = DbContextOptionsBuilder();
SeedInMemoryTestDb(_inMemoryContextOptions);
}
private static DbContextOptions<ApplicationDbContext> DbContextOptionsBuilder()
{
return new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString(),new InMemoryDatabaseRoot())
.Options;
}
Unit Test
[FACT]
public void Test1()
await using var context = new ApplicationDbContext(_contextOptions);
//... Assert.Equal()
[FACT]
public void Test2()
await using var context = new ApplicationDbContext(_inMemoryContextOptions);
//... Assert.Equal()
I have both Setup and Unit Test in 5 or 6 class.
I think I need to re-use the same context for every test but I don't achieve to do that.
[CollectionDefinition("SharedDbContext")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture> { }
public class DatabaseFixture : IDisposable
{
public ApplicationDbContext ApplicationDbContext;
public ApplicationDbContext InMemoryApplicationDbContext;
public DatabaseFixture()
{
// Test for real database READ
var contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer(_connectionString)
.Options;
//// Test InMemory CREATE UPDATE DELETE
var inMemoryContextOptions = DbContextOptionsBuilder();
ApplicationDbContext = new ApplicationDbContext(contextOptions);
InMemoryApplicationDbContext = new ApplicationDbContext(inMemoryContextOptions);
SeedInMemoryTestDb(inMemoryContextOptions);
}
}

How to properly moq ExecuteQuery method within DataContext?

I'm having a difficult time trying to understand how to appropriately return mocked data from a simulated database call in a unit test.
Here's an example method I want to unit test (GetBuildings):
public class BuildingService : IBuildingService {
public IQueryable<Building> GetBuildings(int propertyId)
{
IQueryable<Building> buildings;
// Execution path for potential exception thrown
// if (...) throw new SpecialException();
// Another execution path...
// if (...) ...
using (var context = DataContext.Instance())
{
var Params = new List<SqlParameter>
{
new SqlParameter("#PropertyId", propertyId)
};
// I need to return mocked data here...
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
}
return buildings;
}
}
So GetBuildings calls a stored procedure.
So I need to mock the DataContext, that of which I can override and set a testable instance. So what happens here is, in the above example DataContext.Instance() does return the mocked object.
[TestFixture]
public class BuildingServiceTests
{
private Mock<IDataContext> _mockDataContext;
[SetUp]
public void Setup() {
_mockDataContext = new Mock<IDataContext>();
}
[TearDown]
public void TearDown() {
...
}
[Test]
public void SomeTestName() {
_mockDataContext.Setup(r =>
r.ExecuteQuery<Building>(CommandType.StoredProcedure, "someSproc"))
.Returns(new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable());
DataContext.SetTestableInstance(_mockDataContext.Object);
var builings = BuildingService.GetBuildings(1, 1);
// Assert...
}
Please ignore some of the parameters, like propertyId. I've stripped those out and simplified this all. I simply can't get the ExecuteQuery method to return any data.
All other simple peta-poco type methods I can mock without issue (i.e. Get, Insert, Delete).
Update
DataContext.Instance returns the active instance of the DataContext class, if exists, and if not exists, returns a new one. So the method of test under question returns the mocked instance.
Do not mock DataContext. Because mocking DataContext will produce tests tightly coupled to the implementation details of DataContext. And you will be forced to change tests for every change in the code even behavior will remain same.
Instead introduce a "DataService" interface and mock it in the tests for BuildingService.
public interface IDataService
{
IEnumerable<Building> GetBuildings(int propertyId)
}
Then, you can tests implementation of IDataService agains real database as part of integration tests or tests it agains database in memory.
If you can test with "InMemory" database (EF Core or Sqlite) - then even better -> write tests for BuildingService against actual implementation of DataContext.
In tests you should mock only external resources (web service, file system or database) or only resources which makes tests slow.
Not mocking other dependencies will save you time and give freedom while you refactoring your codebase.
After update:
Based on the updated question, where BuildingService have some execution path - you can still testing BuildingService and abstract data related logic to the IDataService.
For example below is BuildingService class
public class BuildingService
{
private readonly IDataService _dataService;
public BuildingService(IDataService dataService)
{
_dataService = dataService;
}
public IEnumerable<Building> GetBuildings(int propertyId)
{
if (propertyId < 0)
{
throw new ArgumentException("Negative id not allowed");
}
if (propertyId == 0)
{
return Enumerable.Empty<Building>();
}
return _myDataService.GetBuildingsOfProperty(int propertyId);
}
}
In tests you will create a mock for IDataService and pass it to the constructor of BuildingService
var fakeDataService = new Mock<IDataContext>();
var serviceUnderTest = new BuildingService(fakeDataService);
Then you will have tests for:
"Should throw exception when property Id is negative"
"Should return empty collection when property Id equals zero"
"Should return collection of expected buildings when valid property Id is given"
For last test case you will mock IDataService to return expected building only when correct propertyId is given to _dataService.GetBuildingsOfProperty method
In order for the mock to return data is needs to be set up to behave as expected given a provided input.
currently in the method under test it is being called like this
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
Yet in the test the mock context is being setup like
_mockDataContext.Setup(r =>
r.ExecuteQuery<Building>(CommandType.StoredProcedure, "someSproc"))
.Returns(new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable());
Note what the mock is told to expect as parameters.
The mock will only behave as expected when provided with those parameters. Otherwise it will return null.
Consider the following example of how the test can be exercised based on the code provided in the original question.
[Test]
public void SomeTestName() {
//Arrange
var expected = new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable();
_mockDataContext
.Setup(_ => _.ExecuteQuery<Building>(CommandType.StoredProcedure, It.IsAny<string>(), It.IsAny<object[]>()))
.Returns(expected);
DataContext.SetTestableInstance(_mockDataContext.Object);
var subject = new BuildingService();
//Act
var actual = subject.GetBuildings(1);
// Assert...
CollectionAssert.AreEquivalent(expected, actual);
}
That said, the current design of the system under test is tightly coupled to a static dependency which is a code smell and makes the current design follow some bad practices.
The static DataContext which is currently being used as a factory should be refactored as such,
public interface IDataContextFactory {
IDataContext CreateInstance();
}
and explicitly injected into dependent classes instead of calling the static factory method
public class BuildingService : IBuildingService {
private readonly IDataContextFactory factory;
public BuildingService(IDataContextFactory factory) {
this.factory = factory
}
public IQueryable<Building> GetBuildings(int propertyId) {
IQueryable<Building> buildings;
using (var context = factory.CreateInstance()) {
var Params = new List<SqlParameter> {
new SqlParameter("#PropertyId", propertyId)
};
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
}
return buildings;
}
}
This will allow for a proper mock to be created in injected into the subject under test without using a static workaround hack.
[Test]
public void SomeTestName() {
//Arrange
var expected = new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable();
_mockDataContext
.Setup(_ => _.ExecuteQuery<Building>(CommandType.StoredProcedure, It.IsAny<string>(), It.IsAny<object[]>()))
.Returns(expected);
var factoryMock = new Mock<IDataContextFactory>();
factoryMock
.Setup(_ => _.CreateInstance())
.Returns(_mockDataContext.Object);
var subject = new BuildingService(factoryMock.Object);
//Act
var actual = subject.GetBuildings(1);
// Assert...
CollectionAssert.AreEquivalent(expected, actual);
}

Pass multiple mock objects to a method

I have a method CreateAccount to test. I am using Moq for the same.
Under CreateAccount method, there are multiple table insertion methods which belongs to two classes AccountRepository and BillingRepository
I have setup the Moq but don't know how to use multiple moq objects.
Below is some code snippet
Mock<AccountRepository> moq = new Mock<AccountRepository>();
Mock<BillingRepository> moqBill = new Mock<BillingRepository>();
moq.Setup(x => x.AddTable_1(new AddTable_1 { }));
moq.Setup(x => x.AddTable_2(new AddTable_2 { }));
moqBill.Setup(x => x.Table_3());
CreateAccount method takes four parameters and its under ApplicationService class
public class ApplicationService
{
public CreateAccountServiceResponse CreateAccount(AuthenticateApp App, CustomerInfo Customer, ServiceInfo Service, Optional op)
{
// SOME VALIDATION CODE
//.....................
// SOME CODE TO SAVE DATA INTO TABLES
obj_1.AddTable_1(objdata_1);
obj_1.AddTable_2(objdata_2);
obj_2.AddTable_3(objdata_3);
}
}
Please suggest some solution. How can these three methods will be skipped ?
Thanks in advance.
You have to provide some means to inject obj_1 and obj_2, since they seem to represent your instances of AccountRepository and BillingRepository, resp.
Typically, you might want to do this by using constructor injection. Extending the snippet you provided, this might look like this:
public class ApplicationService
{
private readonly AccountRepository _accountRepository;
private readonly BillingRepository _billingRepository;
public ApplicationService(AccountRepository accountRepository, BillingRepository billingRepository)
{
_accountRepository = accountRepository;
_billingRepository = billingRepository;
}
public CreateAccountServiceResponse CreateAccount(AuthenticateApp App, CustomerInfo Customer, ServiceInfo Service, Optional op)
{
// SOME VALIDATION CODE
//.....................
// SOME CODE TO SAVE DATA INTO TABLES
_accountRepository.AddTable_1(objdata_1);
_accountRepository.AddTable_2(objdata_2);
_billingRepository.AddTable_3(objdata_3);
}
}
Now you can inject your mocks into the class under test:
public void CreateAccount_WhenCalledLikeThis_DoesSomeCoolStuff()
{
var accountRepoMock = new Mock<AccountRepository>();
// set it up
var billingRepository = new Mock<BillingRepository>();
// set it up
var appService = new ApplicationService(accountRepoMock.Object, billingRepoMock.Objcet);
// More setup
// Act
var response = appService.CreateAccount(...);
// Assert on response and/or verify mocks
}

Unit Testing with DbContext mock through Service Layer

I'm a beginner at writing unit tests and I have a test I'm trying to get working. I'll start of by explaining what I'm trying to test.
I'm trying to test a method which saves messages in a Mvc 4 project. The method is called SaveMessage and is shown below.
namespace ChatProj.Service_Layer
{
public class UserService : IUserService
{
public MessageContext messageContext = new MessageContext();
public UserService()
{
_messageRepository = new MessageRepository(messageContext);
}
private IMessageRepository _messageRepository;
-> public void SaveMessage(Message message)
{
messageContext.Messages.Add(message);
_messageRepository.Save();
}
The _messageRepository.Save in the SaveMessage method is implemented in my DAL layer MessageRepository and looks like this:
public void Save()
{
context.SaveChanges();
}
This way of saving will seem a bit overcomplicated, but I structured the project this way because I didn't want the service layer (IUserService & UserService) to handle operations that could & should (i think) be handled by the Data Access Layer (IMessageRepository & MessageRepository).
Now comes the tricky part. I've been trying to understand how I could unit test this. This is my try:
namespace ChatProj.Tests
{
[TestFixture]
class MessageRepositoryTests
{
[SetUp]
public void Setup()
{
}
[Test]
public void SaveMessage_SaveWorking_VerifyUse()
{
//Arrange
var userServiceMock = new Mock<UserService>();
var message = new Message { MessageID = 0, Name = "Erland", MessageString = "Nunit Test", MessageDate = DateTime.Now };
var repositoryMock = new Mock<IMessageRepository>();
var contextMock = new Mock<MessageContext>();
MessageRepository messageRepository = new MessageRepository(contextMock.Object);
UserService userService = new UserService();
//Act
userService.SaveMessage(message);
//Assert
repositoryMock.Verify(m => m.Save());
userServiceMock.Verify(m => m.SaveMessage(message));
}
}
I get this error: Imgur link , and I'm not quite sure how to solve it. I've tried looking at several other SO posts but I fail to make the test work.
So I'm wondering, how do I practically get my Unit Test to work?
You should setup your MessageContext properties to return fake data and don't make real Db call with SaveChanges method.
Right now it still tries to access a real DB.
But you can setup only virtual properties or if it will be an inteface.
So the best solution is to extract an interface from your MessageContext and inject it into repository. Then you can easily mock your IMessageContext interface and force it to return appropriate in-memory data.
Take a look at these two lines:
UserService userService = new UserService();
//Act
userService.SaveMessage(message);
You're creating a userService instance, and then immediately saving your message. Now jump into the SaveMessage code.
public void SaveMessage(Message message)
{
messageContext.Messages.Add(message);
_messageRepository.Save();
}
Ok, now you're adding stuff to messageContext, and then calling _messageRepository.Save(). But where are messageContext and _messageRepository instantiated?
public MessageContext messageContext = new MessageContext();
public UserService()
{
_messageRepository = new MessageRepository(messageContext);
}
You're creating them at instantiation. The mocks that you've created in your test aren't being used. Instead of creating instances of these objects in the constructor, you might consider passing them into the UserService constructor as arguments. Then, you can pass in mocked instances in your test.

Mocking method with Action<T> parameter

[unit testing newbie] [c#]
Consider the following scenario:
I'm using Silverlight and calling a WCF service. Silverlight can only call WCF services asynchronously. I build a wrapper around the WCF service so that I can work with Action parameters. (makes the client code a lot cleaner).
So I have an async service that retrieves meeting rooms.
public interface IMeetingRoomService
{
void GetRooms(Action<List<MeetingRoom>> result);
}
Turning GetRooms into List<MeetingRoom> GetRooms() is not an option.
I want to use this service in a ViewModel to set a public property called Rooms.
public class SomeViewModel
{
private readonly IMeetingRoomService _meetingRoomService;
public List<MeetingRoom> Rooms { get; set; }
public SomeViewModel(IMeetingRoomService meetingRoomService)
{
this._meetingRoomService = meetingRoomService;
}
public void GetRooms()
{
// Code that calls the service and sets this.Rooms
_meetingRoomService.GetRooms(result => Rooms = result);
}
}
I want to unit test the implementation of SomeViewModel.GetRooms().
(For this question I quickly wrote the implementation but I'm actually trying to use TDD.)
How do I finish this test?
I'm using NUnit and Moq.
[Test]
public void GetRooms_ShouldSetRooms()
{
var theRooms = new List<MeetingRoom>
{
new MeetingRoom(1, "some room"),
new MeetingRoom(2, "some other room"),
};
var meetingRoomService = new Mock<IMeetingRoomService>();
//How do I setup meetingRoomService so that it gives theRooms in the Action??
var viewModel = new SomeViewModel(meetingRoomService.Object);
viewModel.GetRooms();
Assert.AreEqual(theRooms, viewModel .Rooms);
}
EDIT:
Solution
Read Stephane's answer first.
This is the Test code I ended up writing thanks to stephane's answer:
[Test]
public void GetRooms_ShouldSetRooms()
{
var meetingRoomService = new Mock<IMeetingRoomService>();
var shell = new ShellViewModel(meetingRoomService.Object);
var theRooms = new List<MeetingRoom>
{
new MeetingRoom(1, "some room"),
new MeetingRoom(2, "some other room"),
};
meetingRoomService
.Setup(service => service.GetRooms(It.IsAny<Action<List<MeetingRoom>>>()))
.Callback((Action<List<MeetingRoom>> action) => action(theRooms));
shell.GetRooms();
Assert.AreEqual(theRooms, shell.Rooms);
}
Here is some pseudo code, I haven't run it. But I think that's what you want.
SetupCallback is what you are interested in.
For all the calls to _meetingRoomServiceFake.GetRooms, simply set the _getRoomsCallback to the parameter passed in.
You now have a reference to the callback that you are passing in your viewmodel, and you can call it with whatever list of MeetingRooms you want to test it.
So you can test your asynchronous code almost the same way as synchronous code. it's just a bit more ceremony to setup the fake.
Action<List<MeetingRoom>> _getRoomsCallback = null;
IMeetingRoomService _meetingRoomServiceFake;
private void SetupCallback()
{
Mock.Get(_meetingRoomServiceFake)
.Setup(f => f.GetRooms(It.IsAny<Action<List<MeetingRoom>>>()))
.Callback((Action<List<MeetingRoom>> cb) => _getRoomsCallback= cb);
}
[Setup]
public void Setup()
{
_meetingRoomServiceFake = Mock.Of<IMeetingRoomService>();
SetupCallback();
}
[Test]
public void Test()
{
var viewModel = new SomeViewModel(_meetingRoomServiceFake)
//in there the mock gets called and sets the _getRoomsCallback field.
viewModel.GetRooms();
var theRooms = new List<MeetingRoom>
{
new MeetingRoom(1, "some room"),
new MeetingRoom(2, "some other room"),
};
//this will call whatever was passed as callback in your viewModel.
_getRoomsCallback(theRooms);
}
You could use an AutoResetEvent to handle async calls.
Just initialize it as unset and configure your mock service to set it in the callback.
(IE:
var mockService = new Mock();
mockService.SetUp(x => x.MyMethod()).Returns(someStuff).Callback(() => handle.Set());
)
After that I use hadle.WaitOne(1000) to check if it was called. (IMHO 1000 miliseconds are more than enough to run the async code).
Sorry: This was supposed to go as a reply to the post above... I can't for the life of me figure out how to reply :)

Categories

Resources