1

first time using AutoQuery and I have this problem with unit testing after implementing AutoQuery. It works fine through Swagger manual testing. So I have a get method like this:

public class ItemService : Service
{
public IAutoQueryDb AutoQuery { get; set; }
private readonly IRepository<Item> itemRepository;

public ItemService(IRepository<Item> itemRepository)
    {
        this.itemRepository = itemRepository;
    }

public ItemResponse Get(FindItems query)
    {
        var response = new ItemResponse();
        var q = AutoQuery.CreateQuery(query, Request);
        q.Where(x => !x.IsDeleted);
        response.Offset = q.Offset.GetValueOrDefault(0);
        response.Total = (int)itemRepository.CountByCondition(q);
        var queryResult = AutoQuery.Execute(query, q).Results;
        foreach (var item in queryResult)
        {
            response.MultipleResult.Add(item.ToDto());
        }
        return response;
    }
}

The request/response are built like this:

[Route("/item/{Id}", "GET")]
public class Items : IReturn<ItemResponse>
{
    public Items() : base()
    {
    }
    public int Id { get; set; }
}

[Route("/item", "GET")]
public class FindItems : QueryDb<Item>
{
    public int[] Ids { get; set; }
    public string NameContains { get; set; }
}
public class ItemResponse : BaseResponse<ItemDto>
{
    public ItemResponse()
    {
        MultipleResult = new List<ItemDto>();
    }
}

and the test:

public void GetAllItems()
    {
        SeedDatabase();
        var service = appHost.Container.Resolve<ItemService>();
        var request = new rq.FindItems();
        var response = service.Get(request);
        Assert.NotNull(response);
        Assert.Empty(response.MultipleResult);
    }

The problem is that Request inside CreateQuery method remains null (when I run the app, it's properly filled). So what should I do in xunit test to get Request to be proper object instead of null? Ofc I get null exception on test execution. Is there any mechanism to preset the Request? Thanks for any help.

//////UPDATE: I tried different approach as suggested using built-in client:

[Fact]
    public void CanGetAll()
    {
        var client = new JsonHttpClient(BaseUri);
        var all = client.Get(new FindItem());
        Assert.Empty(all.Results);
    }

The Request is not null anymore but CreateQuery still returns null. I feel like I'm still missing a parameter or a few but I have no idea where. I compared Request object when I run the app through IIS and the one created for unit tests and they look similiar, yet not the same.

//////SOLUTION
JsonServiceClient finally worked. I needed to add AQ Plugin to test setup class and add Results property into Response class to pass the results to QueryResponse instance. No need to change built-in JasonServiceClient at all, default parameters work just fine. I wasn't able to make it work based on BasicRequest, tho. But I got what I needed, that's enough for now.

Mike
  • 151
  • 1
  • 4
  • 14

1 Answers1

1

Note: Services like AutoQuery which depend on an IRequest context is best served by an Integration Test not a Unit Test but you can use BasicRequest to inject an empty IRequest, e.g:

var service = appHost.Container.Resolve<ItemService>();
service.Request = new BasicRequest();

Also a lot of ServiceStack functionality expects to be running a configured AppHost, see the In Memory Database Unit Test for how you can configure a BasicAppHost whilst your tests are running.

Here's an Example of an AutoQuery Unit Test:

You need to create a Custom AutoQuery Implementation since AutoQuery only creates AutoQuery Services for real Services:

[Route("/movies")]
public class QueryMovies : QueryDb<Movie>
{
    public string[] Ratings { get; set; }
}

public class MyQueryServices : Service
{
    public IAutoQueryDb AutoQuery { get; set; }

    public object Any(QueryMovies query)
    {
        var q = AutoQuery.CreateQuery(query, base.Request);
        return AutoQuery.Execute(query, q);
    }
}

Then you can configure your BasicAppHost with the AutoQueryFeature and an In Memory SQLite database with the tables you want to query, e.g:

public class AutoQueryUnitTests
{
    private ServiceStackHost appHost;
    public AutoQueryUnitTests()
    {
        appHost = new BasicAppHost {
            ConfigureAppHost = host => {
                host.Plugins.Add(new AutoQueryFeature());
            },
            ConfigureContainer = container => {
                var dbFactory = new OrmLiteConnectionFactory(
                    ":memory:", SqliteDialect.Provider);
                container.Register<IDbConnectionFactory>(dbFactory);
                using (var db = dbFactory.Open()) {
                    db.DropAndCreateTable<Movie>();
                    db.InsertAll(new[] {
                        new Movie { ... },
                    });
                }
                container.RegisterAutoWired<MyQueryServices>();
            },
        }.Init();
    }
    [OneTimeTearDown] public void OneTimeTearDown() => appHost.Dispose();

    [Test]
    public void Can_execute_AutoQueryService_in_UnitTest()
    {
        var service = appHost.Resolve<MyQueryServices>();
        service.Request = new BasicRequest();
        var response = (QueryResponse<Movie>) service.Any(
            new QueryMovies { Ratings = new[] {"G", "PG-13"} });            
        Assert.That(response.Results.Count, Is.EqualTo(5));
    }
}
mythz
  • 141,670
  • 29
  • 246
  • 390
  • I have a BasicAppHost configured in testsetup class just like you showed but I use xUnit but it's similiar. Like I said it all works fine without AQ. I'm just trying to get unit tests still work for AQ if it's possible. Where do you suggest I should inject an empty Request like you showed? If I do this in test method right under Resolve,, it's still not working, Request in Get method is still null. – Mike Sep 27 '18 at 08:20
  • This is useful for custom implementation of AutoQuery (and since it's custom it should be tested). The full AppHost makes tests run slowly, so BasicAppHost is faster. – specimen Sep 06 '21 at 08:47