GraphQL Testing

What is the problem?

Problem

  • how to test GraphQL queries
  • how to ensure that our tests are reliable
  • how to ensure that our implementation would work after a couple of weeks
  • how to simulate user interaction with API

How it was solved?

  • static files tests
  • no tests

What problems has this solution?

  • stability and immutability of tests
  • escalation of changes in implementation in tests
  • basin on data in database
  • relaying on spotting everything in a pr

How we can solve them?

  • tests to all new/added functionalities
  • stop basin on concrete data
  • good distinction of unit/it/acceptance tests

FsCheck for the rescue

What is FsCheck?

  • Port of QuickCheck
  • Integrated with NUnit, XUnit, Expecto etc.
  • Written in F#

Property-Based testing paradigm(?)

  • difference from unit tests
    • how to test a + b method
  • silver bullet?

Random testing?

  • Are FsCheck tests tottaly random?

FsCheck infrastructure

Test

  • Attribute
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 

[FsCheck.NUnit.Property(Arbitrary = new[] { typeof(TestArbitrary) }, MaxTest = 10)]
public Property Post_Return200(TestObject obj)
{
    var response = DoSomething(obj);

    return response.StatusCode.Equals(HttpStatusCode.OK)
        .ToProperty()
        .And(() => response.Data.Errors.IsEmpty())
        .Label("`DoSomething` should return 200 status code and no" + 
            "error section, but return status code: " +
            $"{response.StatusCode} and errors: {response.Data.Errors}");
}

Generator

  • Arbitrary value
  • How it works?

Shrinker

  • What it does?

Configuration

  • What could be configured?
  • What is the output of some confiugration values?

Problems with FsCheck

  • Async Task
  • F# collections
  • Property-Based technic

Real example

Test

  • code

How input is generated?

  • code

How assertion looks like?

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 

[FsCheck.NUnit.Property(Arbitrary = new[] { typeof(TestArbitrary) }, MaxTest = 10)]
public Property Post_Return200(TestObject obj)
{
    // act

    return assertion
        .ToProperty()
        .Label($"some information");
}

How shrinker works #1

For query which fails because model is not resolved correctly, the first input to the test looks like this:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
car(name: "name of a car")
{
    name
    model
    engine {
        power
    }
}

How shrinker works #2

Then shrinker in a couple of iterations generates inputs without some data, we could see it as "in every iteration remove a single property" (in our example)

How shrinker works #3

So in a first iteration inputs would look like this:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
car(name: "name of a car") {
    model
    engine {
        power
    }
}

car(name: "name of a car") {
    name
    engine {
        power
    }
}

car(name: "name of a car") {
    name
    model
}

How shrinker works #4

and in second iteration it would generate following inputs:

1: 
car(name: "name of a car") { model }

How to spot problem when test fails?

  • outputwindow
  • Replay value of Property attribute

Thanks