How to unit test Durable Functions with CallActivityWithRetryAsync - unit-testing

I've read Microsoft documentation on Unit Testing
Unit testing: https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-unit-testing
So for example, based on the MS documentation, if this is my orchestrator:
[FunctionName("SagaToTestOrchestrator")]
public static async Task<ShippingPrice> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var input = context.GetInput<SagaContext>();
RetryOptions _retryOptions = RetryConfiguration.GetActivityRetryOptions();
// activity to get proper orchestrator for continent for shipping partner
var supplierOrchestratorToRun = await context.CallActivityWithRetryAsync<string>("GetSupplierOrchestratorForContinent", _retryOptions, input.Continent);
// orchestrator to get the price for the shipping address
var priceForShipment =
await context.CallSubOrchestratorAsync<decimal>($"{supplierOrchestratorToRun}Orchestrator", input);
// activity to publish event for Sales / marketing
await context.CallActivityWithRetryAsync("PublishCalculatedPriceActivity", _retryOptions,(input, priceForShipment));
return new ShippingPrice()
{
Shippable = true,
Price = priceForShipment
};
}
[FunctionName("CourierAOrchestrator")]
public static async Task<decimal> CourierAOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context)
{
return 100;
}
[FunctionName("CourierBOrchestrator")]
public static async Task<decimal> CourierBOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context)
{
return 120;
}
[FunctionName("IsContinentSupported")]
public static async Task<bool> IsContinentSupported([ActivityTrigger] string continent, ILogger log)
{
var supportedContinents = new List<string>
{
"North America", "South America", "Europe",
};
return supportedContinents.Contains(continent);
}
[FunctionName("GetSupplierOrchestratorForContinent")]
public static async Task<string> GetSupplierOrchestratorForContinent([ActivityTrigger] string continent, ILogger log)
{
var courier = "";
switch (continent)
{
case "South America":
case "North America":
courier = "CourierA";
break;
case "Europe":
courier = "CourierB";
break;
}
return courier;
}
[FunctionName("PublishCalculatedPriceActivity")]
public static async Task PublishCalculatedPriceActivity([ActivityTrigger] (SagaContext context, decimal price) input, ILogger log)
{
log.LogInformation($"{input.context.Continent}: {input.price}");
}
public class ShippingPrice
{
public bool Shippable { get; set; }
public decimal Price { get; set; }
public string Message { get; set; }
}
public class SagaContext
{
public string Street { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string Continent { get; set; }
}
And my test would be something like this:
public async Task CalculatePriceForAmerica()
{
// Arrange / Given
var orchContext = new SagaContext
{
Continent = "North America"
};
var context = new Mock<IDurableOrchestrationContext>();
// mock the get input
context.Setup(m =>
m.GetInput<SagaContext>()).Returns(orchContext);
// set-up mocks for activity
context.Setup(m
=> m.CallActivityWithRetryAsync<string>("GetSupplierOrchestratorForContinent", _retryOptions, It.IsAny<object>()))
.ReturnsAsync("CourierA");
// set-up mocks for suborchstrators
context.Setup(m =>
m.CallSubOrchestratorAsync<decimal>("CourierAOrchestrator", It.IsAny<string>(), It.IsAny<object>()))
.ReturnsAsync(100);
// ACT / When
var price = await SagaToTestOrchestrator.RunOrchestrator(context.Object);
// Assert / Then
Assert.True(price.Shippable);
Assert.Equal(100, price.Price);
}
Here the issue I am facing is
setup mock data for the GetSupplierOrchestratorForContinent Activity , so ideally it should return "CourierA" as output when the actual Orchestrator method is called but it is returning null.

Related

Bunit 2 way-binding

I have a Search component that implements a debounce timer so it doesn't call ValueChanged (and therefore doesn't update the property tied to it immediately).
My Issue
The bUnit test doesn't appear to two way bind my value I am updating.
Test code
private string StringProperty { get; set; }
[Fact]
public async Task AfterDebounce_ValueUpdates()
{
var myString = "";
var cut = RenderComponent<Search>(parameters => parameters
.Add(p => p.Value, StringProperty)
.Add(p => p.ValueChanged, (s) => myString = s)
);
var input = cut.Find("input");
input.Input("unit test");
Assert.Equal("unit test", cut.Instance.Value);
Assert.NotEqual("unit test", myString);
//Assert.NotEqual("unit test", StringProperty);
await Task.Delay(5000);
Assert.Equal("unit test", myString);
//Assert.Equal("unit test", StringProperty);
}
I would have expected the commented out parts to work (as they are doing the same thing as the ValueChanged to update the property), but they fail.
The component
public class Search : ComponentBase
{
[Parameter] public string? Value { get; set; }
[Parameter] public EventCallback<string> ValueChanged { get; set; }
[DisallowNull] public ElementReference? Element { get; protected set; }
private System.Timers.Timer timer = null;
protected string? CurrentValue {
get => Value;
set {
var hasChanged = !EqualityComparer<string>.Default.Equals(value, Value);
if (hasChanged)
{
Value = value;
DisposeTimer();
timer = new System.Timers.Timer(350);
timer.Elapsed += TimerElapsed_TickAsync;
timer.Enabled = true;
timer.Start();
}
}
}
private void DisposeTimer()
{
if (timer != null)
{
timer.Enabled = false;
timer.Elapsed -= TimerElapsed_TickAsync;
timer.Dispose();
timer = null;
}
}
private async void TimerElapsed_TickAsync(
object sender,
EventArgs e)
{
await ValueChanged.InvokeAsync(Value);
}
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(10, "input");
builder.AddAttribute(20, "type", "text");
builder.AddAttribute(60, "value", BindConverter.FormatValue(CurrentValue));
builder.AddAttribute(70, "oninput", EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValue = __value, CurrentValue));
builder.AddElementReferenceCapture(80, __inputReference => Element = __inputReference);
builder.CloseElement();
}
}
How it is used:
It may be used like this where the grid will update whenever Query is updated.
<Search #bind-Value=Query />
<Grid Query=#Query />
#code {
private string? Query { get; set; }
}
This works fine in practice, but when testing I am having issues.
I tried locally on my machine, and the test passed.
Here is a simplified version of your component, that only calls TimerElapsed_TickAsync one time per value change and not every time the timer runs out (AutoReset defaults to true), and two different ways to write the test that both pass on my machine:
public class Search : ComponentBase, IDisposable
{
private readonly Timer timer;
[Parameter] public string? Value { get; set; }
[Parameter] public EventCallback<string> ValueChanged { get; set; }
[DisallowNull] public ElementReference? Element { get; protected set; }
public Search()
{
timer = new Timer(350);
timer.Elapsed += TimerElapsed_TickAsync;
timer.Enabled = true;
timer.AutoReset = false;
}
protected string? CurrentValue
{
get => Value;
set
{
var hasChanged = !EqualityComparer<string>.Default.Equals(value, Value);
if (hasChanged)
{
RestartTimer();
Value = value;
}
}
}
private void RestartTimer()
{
if (timer.Enabled)
timer.Stop();
timer.Start();
}
private void TimerElapsed_TickAsync(object sender, EventArgs e)
=> ValueChanged.InvokeAsync(Value);
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(10, "input");
builder.AddAttribute(20, "type", "text");
builder.AddAttribute(60, "value", BindConverter.FormatValue(CurrentValue));
builder.AddAttribute(70, "oninput", EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValue = __value, CurrentValue));
builder.AddElementReferenceCapture(80, __inputReference => Element = __inputReference);
builder.CloseElement();
}
public void Dispose() => timer.Dispose();
}
And the C# version of the test:
[Fact]
public async Task AfterDebounce_ValueUpdates()
{
var expected = "test input";
var count = 0;
var value = "";
var cut = RenderComponent<Search>(parameters => parameters
.Add(p => p.Value, value)
.Add(p => p.ValueChanged, (s) =>
{
value = s;
count++;
})
);
cut.Find("input").Input(expected);
await Task.Delay(350);
Assert.Equal(1, count);
Assert.Equal(expected, value);
}
and the .razor version of the test (aka. written in a .razor file):
#inherits TestContext
#code
{
[Fact]
public async Task AfterDebounce_ValueUpdates()
{
var expected = "test input";
var value = "";
var cut = Render(#<Search #bind-Value="value" /> );
cut.Find("input").Input(expected);
await Task.Delay(350);
Assert.Equal(expected, value);
}
}

Perform xUnit Moq test to create record and save it to DbContext

I am writing test for Web API application written in .NET CORE 3.1. I am using xUnit, AutoFixture & Moq for testing. I have a class that creates a new school instance in the database using Entity Framework/ DbContext. My question is how to mock dbContext & save changes, further my School DataModel has one: many relationships with SchoolBranch DataModel. I have followed this tutorial https://learn.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking
Error
Message:
Moq.MockException :
Expected invocation on the mock once, but was 0 times: m => m.Add<School>(It.IsAny<School>())
Performed invocations:
Mock<SchoolDbContext:1> (m):
No invocations performed.
Stack Trace:
Mock.Verify(Mock mock, LambdaExpression expression, Times times, String failMessage)
Mock`1.Verify[TResult](Expression`1 expression, Times times)
CreateSchoolCommandTest.ExecuteMethod_ShouldReturnNewGuidId_IfSuccess() line 50
School
public class School
{
public School()
{
this.SchoolBranches = new HashSet<SchoolBranch>();
}
public Guid SchoolID { get; set; }
public string Name { get; set; }
public ICollection<SchoolBranch> SchoolBranches { get; set; }
}
SchoolBranch
public class SchoolBranch
{
public SchoolBranch()
{
}
public Guid SchoolBranchID { get; set; }
public Guid SchoolID { get; set; }
public string Address { get; set; }
public int PhoneNumber { get; set; }
public School School { get; set; }
}
CreateSchool Class
public class CreateSchool : BaseCommand<Guid>, ICreateSchool
{
public SchoolDto SchoolDtos { get; set; }
public CreateSchool(IAppAmbientState appAmbient) : base(appAmbient) { }
public override Guid Execute()
{
try
{
var schoolId = Guid.NewGuid();
List<SchoolBranch> schoolBranches = new List<SchoolBranch>();
foreach(var item in SchoolDtos.SchoolBranchDtos)
{
schoolBranches.Add(new SchoolBranch()
{
SchoolBranchID = Guid.NewGuid(),
SchoolID = schoolId,
Address = item.Address,
PhoneNumber = item.PhoneNumber
});
}
var school = new School()
{
SchoolID = schoolId,
Name = SchoolDtos.Name,
SchoolBranches = schoolBranches
};
schoolDbContext.Schools.Add(school);
schoolDbContext.SaveChanges();
return school.SchoolID;
}
catch(Exception exp)
{
appAmbientState.Logger.LogError(exp);
throw;
}
}
}
Test Class
public class CreateSchoolCommandTest
{
private readonly ICreateSchool sut;
private readonly Mock<IAppAmbientState> appAmbientState = new Mock<IAppAmbientState>();
[Fact]
public void ExecuteMethod_ShouldReturnNewGuidId_IfSuccess()
{
//Arrange
var fixture = new Fixture();
var schoolDtoMock = fixture.Create<SchoolDto>();
var schoolDbSetMock = new Mock<DbSet<School>>();
var schoolBranchDbSetMock = new Mock<DbSet<SchoolBranch>>();
var schoolDbContextMock = new Mock<SchoolDbContext>();
//schoolDbSetMock.Setup(x => x.Add(It.IsAny<School>())).Returns((School s) => s); // this also did not work
schoolDbContextMock.Setup(m => m.Schools).Returns(schoolDbSetMock.Object);
//Act
sut.SchoolDtos = schoolDtoMock;
var actualDataResult = sut.Execute();
// Assert
Assert.IsType<Guid>(actualDataResult);
schoolDbContextMock.Verify(m => m.Add(It.IsAny<School>()), Times.Once());
schoolDbContextMock.Verify(m => m.SaveChanges(), Times.Once());
}
BaseCommand (DbContext is created here)
public abstract class BaseCommand<T>
{
protected SchoolDbContext schoolDbContext;
protected IAppAmbientState appAmbientState { get; }
public BaseCommand(IAppAmbientState ambientState)
{
this.schoolDbContext = new SchoolDbContext();
this.appAmbientState = ambientState;
}
public abstract T Execute();
}
For fix Error
You made just a little mistake. Insted of
schoolDbContextMock.Verify(m => m.Add(It.IsAny<School>()), Times.Once());
schoolDbContextMock.Verify(m => m.SaveChanges(), Times.Once());
You should have
schoolDbSetMock.Verify(m => m.Add(It.IsAny<School>()), Times.Once());
schoolDbContextMock.Verify(m => m.SaveChanges(), Times.Once());
Because you use method Add() on schoolDbContext.Schools not on schoolDbContext
For injecting dbContext
Your BaseCoommand class constructor should look like this:
public BaseCommand(IAppAmbientState ambientState, SchoolDbContext schoolDbContext)
{
this.schoolDbContext = schoolDbContext;
this.appAmbientState = ambientState;
}
Your CreateSchool class constructor:
public CreateSchool(IAppAmbientState appAmbient, SchoolDbContext schoolDbContext) : base(appAmbient, schoolDbContext) { }
And next in test you should initialize CreateSchool in test like this:
var sut = new CreateSchool(ambientState, schoolDbContextMock.Object);
And it will work

Can't show json data (Xamarin.form)

I think this is because my json gives me an array, but I don't know how it solved, this is what I did. (I'm new at this)
running in Visual Studio 2019 (xamarin.form) with web services but the url is hidden for security, so don't pay attention in that.
---my-json---
{
"cuentas":[
{
"cuenta":"0500",
"usuario":41
},
{
"cuenta":"0508",
"usuario":6
},
{
"cuenta":"0522",
"usuario":41
},
{
"cuenta":"0532",
"usuario":41
},
null
]
}
---WSClient.cs---
class WSClient
{
public async Task<T> Post<T>(string url, StringContent c)
{
var client = new HttpClient();
var response = await client.PostAsync(url, c);
var json = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(json);
}
}
----Cuenta.cs---
public class Cuenta
{
public string cuenta { get; set; }
public int usuario { get; set; }
}
------MainPage.xaml.cs-----
private async void BtnCall_Clicked(object sender, EventArgs e)
{
WSClient client = new WSClient();
string dato = "";
StringContent content = new StringContent(dato, Encoding.UTF8, "application/json");
var result = await client.Post<Cuenta>("http://www.***", content);
if (result != null) {
lblCuenta.Text = result.cuenta;
lblUsuario.Text = result.cuenta;
}
}
It doesn't show me anything and it doesn't give me any mistakes... any advice?
( I can see the json in the console if I use WriteLine in "WSClient" )
your class should look like this (using json2csharp.com)
public class Cuenta
{
public string cuenta { get; set; }
public int usuario { get; set; }
}
public class RootObject
{
public List<Cuenta> cuentas { get; set; }
}
var result = await client.Post<RootObject>("http://www.***", content);

New kendo sheduler events don't call controller methods

I'm trying to add a new scheduler event to database. It's added to the scheduler datasource and its visible in scheduler, but isn't call controller Create method.
Edit or delete newly added event also don't call controller methods, but change datasource.
Already exists events work well.
Model:
public class ResourceSchedulerModel : ISchedulerEvent
{
public string Title { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
public string Description { get; set; }
public bool IsAllDay { get; set; }
public string RecurrenceRule { get; set; }
public string RecurrenceException { get; set; }
public string EndTimezone { get; set; }
public string StartTimezone { get; set; }
public int BTS_Id { get; set; }
public ResourceSchedulerModel() { }
public ResourceSchedulerModel(BusyTimeSlot bts)
{
BTS_Id = bts.BTS_Id;
Start = bts.BTS_From;
End = bts.BTS_To;
Title = bts.BTS_Name;
Description = bts.BTS_Description;
ResId = bts.BTS_RES_Id;
}
}
Controller methods:
public virtual JsonResult Read([DataSourceRequest] DataSourceRequest request)
{
EntityWrapper ew = new EntityWrapper();
List<BusyTimeSlot> btss = ew.GetAllBusyTimeSlots();
List<ResourceSchedulerModel> sm = new List<ResourceSchedulerModel>();
foreach (BusyTimeSlot b in btss)
sm.Add(new ResourceSchedulerModel(b));
return Json(sm.ToDataSourceResult(request));
}
public virtual JsonResult Destroy([DataSourceRequest] DataSourceRequest request, ResourceSchedulerModel task)
{
if (ModelState.IsValid)
{
// delete
}
return Json(new[] { task }.ToDataSourceResult(request, ModelState));
}
public virtual JsonResult Create([DataSourceRequest] DataSourceRequest request, ResourceSchedulerModel task)
{
if (ModelState.IsValid)
{
//add
}
return Json(new[] { task }.ToDataSourceResult(request, ModelState));
}
public virtual JsonResult Update([DataSourceRequest] DataSourceRequest request, ResourceSchedulerModel task)
{
if (ModelState.IsValid)
{
// edit
}
return Json(new[] { task }.ToDataSourceResult(request, ModelState));
}
View:
#(Html.Kendo().Scheduler<SchedulerTry.Models.ResourceSchedulerModel>()
.Name("scheduler")
.Date(new DateTime(2014, 10, 11))
.MinorTickCount(1)
.Views(views =>
{
views.DayView();
views.WeekView(weekView => weekView.Selected(true));
views.MonthView();
views.AgendaView();
})
.DataSource(d => d
.Model(m => {
m.Id(f => f.BTS_Id);
m.Field(f => f.Title).DefaultValue("No title");
})
.Read("Read", "Resource")
.Create("Create", "Resource")
.Destroy("Destroy", "Resource")
.Update("Update", "Resource")
)
)
In addition, when I try close or cancel the edit window of new event I get an error
Uncaught TypeError: Cannot read property 'BTS_Id' of undefined kendo.all.min.js:11
Try updating the BTS_Id as below intialization as below:
public int bTS_Id;
public string BTS_Id
{
get
{
return bTS_Id;
}
set
{
bTS_Id = value;
}
}
My bad, I have another Create method in controller.

Mocking ApiController which has Unit of work Dependency

I have a Apicontroller Which has dependency on unit of work object. How to write a test case for mocking ApiController Which has dependency on unit of work implemented in ApiController constructor.
Here is the code:
ApiController:
public class UserController : ApiController
{
public IUoW UoW { get; set; }
// GET api/user
public UserController(IUoW uow)
{
UoW = uow;
}
public IEnumerable<Users> Get()
{
return UoW.Users.Getall();
}
}
The Test case :
[TestMethod]
public void TestApiController()
{
var userManager = new Mock<IUoW>();
userManager.Setup(s => s.Users);
var controller = new UserController(userManager.Object);
var values = controller.Get();
Assert.IsNotNull(values);
}
The Users Class which has been mentioned here in UoW.Users is
public class UoW:IUoW,IDisposable
{
private MvcWebApiContext DbContext { get; set; }
protected IRepositoryProvider RepositoryProvider { get; set; }
private IRepository<T> GetStandardRepo<T>() where T : class
{
return RepositoryProvider.GetRepositoryForEntityType<T>();
}
public IRepository<Users> Users
{
get { return GetStandardRepo<Users>(); }
}
}
and the Users class itself is
[Table("UserProfile")]
public class Users
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
[DataType(DataType.PhoneNumber)]
public long Mobile { get; set; }
}
When I am trying to debug the test case , the Test case shows the object UoW.Users is null in UserController. Its obvious its not initializing through WebActivator since global.asax isnt invoked here through mock. Now how to write a successful test scenario in this context so that the WebApicontroller returns me the users object with data ?
Are you using Moq?
As I cannot see what type the UoW.Users property is I cannot demo how to mock it properly (updated IRepository) but that should be mocked and the GetAll method stubbed to return a sample list of users.
Updated
var userManager = new Mock<IUoW>();
userManager.Setup(s => s.Users).Returns(()=>
{
var userReposisitory = new Mock<IRepository<Users>>();
userReposisitory.Setup(ur => ur.GetAll()).Returns(()=> {
var listOfUsers = new List<Users>();
listOfUsers.Add(new Users { FirstName = "Example" });
return listOfUsers.AsQueryable();
});
return userReposisitory.Object;
});
var controller = new UserController(userManager.Object);
var result = controller.Get();
Assert.IsNotNull(result);
Assert.IsTrue(result.Count() > 0);