markvincze / Stubbery

Library for creating Api stubs in .NET. https://markvincze.github.io/Stubbery/
MIT License
74 stars 14 forks source link

Provide helper method to set up a condition for a query string argument #9

Open markvincze opened 6 years ago

markvincze commented 6 years ago

We can already set up preconditions on the Route with the syntax

sut.Request(HttpMethod.Get)
    .IfRoute("/testget")
    ...

But we don't have any way to have a precondition for a specific query string argument. (We can do .IfRoute("/testget?arg1=foo&arg2=bar"), but it's not really intuitive.)

We should introduce a new condition method called IfQueryArg, which would check the value of a specific query string argument.

Example usage:

sut.Request(HttpMethod.Get)
    .IfRoute("/testget")
    .IfQueryArg("arg1", "foo")
    .IfQueryArg("arg2", "bar")
    ...
dstj commented 5 years ago

I think it would also be important to support retrieving query string arguments for use in the response, similar to the Route arguments.

markvincze commented 5 years ago

Hey @dstj,

Sorry for the late response: I think this is already possible, you can do the following:

stub.Get(
    "/test",
    (req, args) => $"Value of query arg: {args.Query.myarg}");

Or is this not what you meant?

dstj commented 5 years ago

Hi @markvincze,

Does IfQueryArg() work? Because I've got the following and I see that using the querystring company/id=123 also gets in this mock...

_stub.Get()
    .IfRoute("/some/route")
    .IfQueryArg("fields", "id,name")
    .IfQueryArg("conditions", "company/id=456 and active=true")
    .Response((req, args) => {
    ...

Also, I remembered what I meant by "retrieving query string arguments for use in the response, similar to the Route arguments." above. I meant doing something like this:


_stub.Get()
    .IfRoute("/some/route")
    .IfQueryArg("conditions", "company/id={companyId} and active=true")
    .Response((req, args) => {
        var companyId = args.Query.companyId
    });
dstj commented 5 years ago

Hi again @markvincze, here's more information. Two IfQueryArg() do not work. If one is met, then the others seems ignored (order of IfQueryArg() do not seems relevant).

Here's a simple example to reproduce the problem:

public class TestClass
{
    private Stub _stub;

    [SetUp]
    public void SetUp()
    {
        _stub = new Stub();
        _stub.Start();
    }

    [TearDown]
    public void TearDown()
    {
        _stub.Dispose();
    }

    [TestCase("123", "123")]
    [TestCase("456", "456")]
    [TestCase("789", "catch all")]
    public async Task GetWithQueryArgs(string id, string expectation)
    {
        var encodedConditions = HttpUtility.UrlEncode($"company/id={id}");
        var uri = $"{_stub.Address}/test?fields=name&conditions={encodedConditions}";

        using (var httpClient = new HttpClient())
        using (var httpMessage = new HttpRequestMessage(HttpMethod.Get, uri))
        using (var httpResponse = await httpClient.SendAsync(httpMessage)) {
            var actual = await httpResponse.Content.ReadAsStringAsync();
            Assert.AreEqual(expectation, actual);
        }
    }
}

public class Stub : Stubbery.ApiStub
{
    public Stub()
    {
        Get()
            .IfRoute("/test")
            .IfQueryArg("fields", "name")
            .IfQueryArg("conditions", "company/id=123")
            .Response((req, args) => "123");

        Get()
            .IfRoute("/test")
            .IfQueryArg("fields", "name")
            .IfQueryArg("conditions", "company/id=456")
            .Response((req, args) => "456");

        Get()
            .Response((req, args) => "catch all");
    }
}
markvincze commented 5 years ago

Hi @dstj,

This example:

_stub.Get()
    .IfRoute("/some/route")
    .IfQueryArg("fields", "id,name")
    .IfQueryArg("conditions", "company/id=456 and active=true")
    .Response((req, args) => {
    ...

I'm not sure I understand. You expect the "company/id=456 and active=true" string to be interpreted as some sort of query language? The library cannot do anything like that at the moment, it just takes it as a string verbatim.
So in this case it would match if the query string argument condition contained the value company%2Fid%3D456%20and%20active%3Dtrue.

I understand the other issue about anding and oring conditions together, I'll try to think of a way to introduce this in the interface somehow, I'll keep you updated.

dstj commented 5 years ago

@markvincze, true, I guess the real problem with the conditions=company/id=456 and active=true is not Stuberry but the actual server API itself that uses complex querystring parsing rules. Let's drop this. ;)

Thanks for thinking about the OR and AND issue.