how to mock an async method with an anonymous return type

1 Answer 203 Views
Async Mocking
Garrick
Top achievements
Rank 1
Garrick asked on 21 Apr 2023, 10:19 PM

We are adding UI unit tests for our Blazor project and diving into evaluating your JustMock and your testing framework. i am trying to create a test that invokes AddReleaseAsync method, but nothing that I have tried avoids a compile error, that I am showing below. We have a form that has a Blazor grid, initially populated with the data returned from GetReleasesAsync method, and then we add a new row using   AddReleaseAsync expecting to see an additional data row. A simple test that uses only GetReleasesAsync  method and checks that 2 data rows are returned works fine. But I can't compile the below test for adding a row. How do I mock an async method returning an anonymous type? We are using a pattern returning <string message, IEnumerable<T> data> quite extensively in our services to pass info/error Message and Data to the UI. Is it possible to mock the AddReleaseAsync and if so, how ? 

P.S. I understand that test is not quite 'complete', but I can't get past the compile error to properly write the test


Given this interface

    public interface IApiLogic
    {

        Task<IEnumerable<Release>> GetReleasesAsync();  // gets all data
        Task<(string Message, IEnumerable<Release>? Releases)> AddReleaseAsync(Release release);  
    }

And the below test method, 

    

      [TestMethod]
        public void CanAddnewRelease()
        {
            // Arrange
            var apiMock = Mock.Create<IApiLogic>();
            Mock.Arrange(() => apiMock.GetReleasesAsync()).ReturnsAsync(DummyReleases(2));
           

            var response =  new {  Message = "ok", Releases =  DummyReleases(3)};

            // 1. Tried this (and many other things, but always get a similar error)
            Mock.Arrange(() => apiMock.AddReleaseAsync(Arg.IsAny<Release>())).ReturnsAsync(response);

/* I get compile error for the above line

Error CS1503 Argument 2: cannot convert from '<anonymous type: string Message, System.Collections.Generic.IEnumerable<SDCSSPxte.Shared.Entities.Release> Releases>' to 'System.Func<(string Message, System.Collections.Generic.IEnumerable<SDCSSPxte.Shared.Entities.Release> Releases)>'

*/

            // 2. Tried this 

            Mock.Arrange(() => apiMock.AddReleaseAsync(Arg.IsAny<Release>())).Returns(Task.FromResult<(string Message, IEnumerable<Release>? Releases)>(response));

/* I get compile error on the above line

Error CS1503 Argument 1: cannot convert from '<anonymous type: string Message, System.Collections.Generic.IEnumerable<SDCSSPxte.Shared.Entities.Release> Releases>' to '(string Message, System.Collections.Generic.IEnumerable<SDCSSPxte.Shared.Entities.Release>? Releases)'

*/

            var ctx = new Bunit.TestContext();
            ctx.JSInterop.Mode = JSRuntimeMode.Loose;
            ctx.Services.AddSingleton<IApiLogic>(apiMock);
            ctx.Services.AddTelerikBlazor();

            var rootComponentMock = Mock.Create<TelerikRootComponent>();

            // Act
            var cut = ctx.RenderComponent<SDCSSPxte.Client.Pages.Admin.Release>(parameters => parameters.AddCascadingValue<TelerikRootComponent>(rootComponentMock));
            var addButton = cut.FindAll(".k-button-text")[0];
            addButton.Click();


            // Assert
            var dataRows = cut.FindAll("tbody tr");
            dataRows.Count.Should().Be(3);
            dataRows[0].ChildNodes.FirstOrDefault(n => n.TextContent.Trim() == "ReleaseComment for r1").Should().NotBeNull();
            dataRows[1].ChildNodes.FirstOrDefault(n => n.TextContent.Trim() == "ReleaseComment for r2").Should().NotBeNull();
            dataRows[2].ChildNodes.FirstOrDefault(n => n.TextContent.Trim() == "ReleaseComment for r3").Should().NotBeNull();     

  }

//  You should be able to infer what the Release class is, it is in a  separate project, SDCSSPxte.Shared.Entities. namespace)

        private List<Release> DummyReleases (int count)
        {
            List<Release> releases = new();
            for (int c=0; c < count; c++)
            {
                releases.Add(new Release()
                {
                    ReleaseKey = 1,
                    ReleaseDate = DateTime.Now.Date,
                    ReleasePackageName = $"ReleasePackageName{c+1}",
                    Comment = $"ReleaseComment for r{c+1}"
                });
            }
            return releases;
        }

 


1 Answer, 1 is accepted

Sort by
0
Ivo
Telerik team
answered on 25 Apr 2023, 04:05 PM

Hello Garrick,

It turns out that JustMock handles anonymous types in a wrong way.  In this particular case, it creates a mock having a method AddReleaseAsync with a return type Tuple.

If you change the arrangement in the following way:

Mock.Arrange(() => apiMock.AddReleaseAsync(Arg.IsAny<Release>())).Returns(() => Task.FromResult(response));

the code can be compiled, but this time the test will fail. The bad news is that I do not see any workaround and the moment, so a fix needs to be provided in order to handle this use case. For the purpose of further activities on this topic, I have created a dedicated item in our Feedback portal, by subscribing to it you will be notified for status updates.

I would like to apologize for the inconvenience and hope that we manage to find a solution soon.

Regards,
Ivo
Progress Telerik

A brand new JustMock course was just added to the Virtual Classroom - the free self-paced technical training portal that gets you up to speed with Telerik and Kendo UI products! Check it out at https://learn.telerik.com.
Tags
Async Mocking
Asked by
Garrick
Top achievements
Rank 1
Answers by
Ivo
Telerik team
Share this question
or