Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xUnit Theory with async MemberData

I have a unit test project using xUnit.net v.2.3.1 for my ASP.NET Core 2.0 web app.

My test should focus on testing a given DataEntry instance: DataEntry instances are generated by the async method GenerateData() in my DataService class, which looks like:

public class DataService {
    ...
    public async Task<List<DataEntry>> GenerateData() {
        ...
    }
    ...
}

I am writing this test case as a Theory so my test can focus on a DataEntry instance at a time. Here is the code:

[Theory]
[MemberData(nameof(GetDataEntries))]
public void Test_DataEntry(DataEntry entry) {

    // my assertions
    Assert.NotNull(entry);
    ...

}

public static async Task<IEnumerable<object[]>> GetDataEntries() {

    var service = new DataService();
    List<DataEntry> entries = await service.GenerateData().ConfigureAwait(false);

    return entries.Select(e => new object[] { e });

}

However, I get the following error at compile time:

MemberData must reference a data type assignable to 'System.Collections.Generic.IEnumerable<object[]>'. The referenced type 'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<object[]>>' is not valid.

From the error description, it seems xUnit.net does not allow MemberData to use an async static method, like my GetDataEntries() one. Is there any functionality gap in xUnit.net I should be aware of?

Now, I know I could switch my Theory into a Fact and loop through each DataEntry in the list returned by my DataService, however I would prefer to keep a Theory setup as my test would be cleaner and focused on DataEntry instead of List<DataEntry>.

Question: is there any way in xUnit.net to let my Theory get data from my DataService async API? Please, note the DataService class cannot be changed nor extended to provide data synchronously.

EDIT

I am looking for a way through async/await and would prefer to avoid any usage of blocking calls such as Task<T>.Result e.g. on my GenerateData() method, as the underlying thread will be blocked til the operation completes. This is relevant in my test project as I have other similar test cases where data should be retrieved in the same way and therefore I want to avoid ending up with too many blocking calls, but instead keeping the async/await propagation.

like image 743
smn.tino Avatar asked May 04 '18 08:05

smn.tino


People also ask

What is xUnit theory with classdata?

xUnit Theory With ClassData ClassData is another attribute that we can use with our theory, with ClassData we have more flexibility and less clutter: public class TestDataGenerator : IEnumerable < object []>

What are the different types of xUnit tests?

xUnit Theory: Working With InlineData, MemberData, ClassData xUnit support two different types of unit test, Fact and Theory. We use xUnit Fact when we have some criteria that always must be met, regardless of data.

Can I use [memberdata] with a [theory] test?

using a [Theory] test with [MemberData] which reference a method works fine. But if that method has optional parameters then the full signature call is required in [MemberData]-Attribute.


1 Answers

Until xUnit allows async theory data, you can use Task<T> instances as theory data and await them inside the test method (note that test methods can be async):

public static IEnumerable<object> GetDataEntries() {
    var service = new DataService();
    yield return new object[] { service.GenerateData() };
}

[Theory]
[MemberData(nameof(GetDataEntries))]
public async Task Test_DataEntry(Task<List<DataEntry>> task) {
    List<DataEntry> entries = await task;

    for (int i = 0; i < entries.Count; i++) {
        // my assertions
        Assert.NotNull(entries[i]);
    }
}
like image 200
JosephDaSilva Avatar answered Sep 17 '22 13:09

JosephDaSilva