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);
}
}
Related
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.
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
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);
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.
I am using Rhino mock for mocking in my test methods. Could someone please see the TODO part in the test method and help me to mock it?
This is my service interface:
public interface ICustomersServiceAgent
{
void GetCustomers(EventHandler<GetCustomersCompletedEventArgs> callback);
void SaveCustomer(POC.Model.Customer cust, EventHandler<SaveCustomerCompletedEventArgs> callback);
}
This is my ViewModel
public class CustomerVM : ViewModelBase
{
private Model.Customer _curentCustomer;
private RelayCommand _saveCommand;
private ICustomersServiceAgent ServiceAgent { get; set; }
private bool isSaved;
private RelayCommand _calculateAgeCommand;
private Decimal age;
private DateTime dateOfBirth;
public CustomerVM(ICustomersServiceAgent serviceAgent)
{
if (serviceAgent == null)
{
ServiceAgent = ServiceManager.GetCustomerServiceManagement();
}
else
{
ServiceAgent =serviceAgent;
}
WireCommands();
}
// if curent object is null then it should be intialize
public Model.Customer CurentCustomer
{
get { return _curentCustomer ?? (_curentCustomer = new Model.Customer()); }
set
{
if (_curentCustomer != value)
{
_curentCustomer = value;
OnPropertyChanged("CurentCustomer");
}
}
}
public RelayCommand CalculateAgeCommand
{
get;
private set;
}
private void WireCommands()
{
SaveCustomerCommand = new RelayCommand(SaveCustomer);
SaveCustomerCommand.IsEnabled = true;
CalculateAgeCommand = new RelayCommand(CalculateAge);
}
private void SaveCustomer()
{
var cus = CurentCustomer;
ServiceAgent.SaveCustomer(cus, (s, e) =>
{
IsSaved = e.Result;
});
}
private void CalculateAge()
{
Age = DateTime.Now.Year - DateOfBirth.Year;
}
public RelayCommand SaveCustomerCommand
{
get;
private set;
}
public bool IsSaved
{
get { return isSaved; }
set
{
isSaved = value;
OnPropertyChanged("IsSaved");
}
}
public decimal Age
{
get { return age; }
set {
age = value;
OnPropertyChanged("Age");
}
}
public DateTime DateOfBirth
{
get { return dateOfBirth; }
set {
dateOfBirth = value;
OnPropertyChanged("DateOfBirth");
}
}
}
I want to test the SaveCustomerCommand in ViewModel above.
So In the my test method, I want to mock the void SaveCustomer(POC.Model.Customer cust, EventHandler<SaveCustomerCompletedEventArgs> callback) method in the ICustomersServiceAgent interface.
This is my test method, see the ToDo part
[TestMethod]
public void SaveCustomerCommandTest()
{
var customerServiceMock = MockRepository.GenerateMock<ICustomersServiceAgent>();
var customerVM = new POC.SilverlightClient.ViewModel.CustomerVM(customerServiceMock);
// TO do : Code to mock SaveCustomer method ///////////////////////////////////
var saveCustomerCommand = customerVM.SaveCustomerCommand;
saveCustomerCommand.Execute(null);
Assert.IsTrue(customerVM.IsSaved);
}
Could someone please explain how I can do this?
I don't see why you need to mock SaveCustomer. All the private SaveCustomer method does is invoke the ServiceAgent service which you already are mocking. I assume the RelayCommand class invokes the delegate you're sending in the constructor parameter which is the call to SaveCustomer. Have you tried running the unit test in your question as-is?
Try following:
customerServiceMock.Stub(sa => sa.SaveCustomer(Arg<POC.Model.Customer>.Is.Anything, Arg<EventHandler<SaveCustomerCompletedEventArgs>>.Is.Anything)).WhenCalled(invocation =>
{
((EventHandler<SaveCustomerCompletedEventArgs>)invocation.Arguments[1])(
customerServiceMock,
new SaveCustomerCompletedEventArgs { Result = true });
});