When you use [Theory]
together with [InlineData]
it will create a test for each item of inline data that is provided. However, if you use [MemberData]
it will just show up as one test.
Is there a way to make [MemberData]
tests show up as multiple tests?
For now, ReSharper can show all MemberData tests with custom parameters when your custom classes overrides
ToString()
.For example :
Permission
overridesToString()
, then in ReSharper Test Session Explorer:In my recent project I experienced the very same problem and after some research the solution which I came up with is as follows:
Implement your custom MyTheoryAttribute extending FactAttribute along with MyTheoryDiscoverer implementing IXunitTestCaseDiscoverer and several custom MyTestCases extending TestMethodTestCase and implementing IXunitTestCase to your liking. Your custom test cases should be recognized by MyTheoryDiscoverer and used to encapsulate your enumerated theory test cases in form visible to Xunit framework even if values passed are not serialized natively by Xunit and do not implement IXunitSerializable.
What is most important there is no need to change your precious code under test!
It's a bit of work to do but since it was done already by me and is available under MIT license feel free to use it. It is part of DjvuNet project which is hosted on GitHub.
Direct link to the relevant folder with Xunit support code is below:
DjvuNet test support code
To use it either create separate assembly with this files or include them directly into your test project.
Usage is exactly the same as with Xunit TheoryAttribute and both ClassDataAttribute and MemberDataAttribute are supported i.e.:
Credit goes as well to another developer but unfortunately I cannot find his repo on github
I spent a lot of time trying to figure this one out in my project. This related Github discussion from @NPadrutt himself helped a lot, but it was still confusing.
The tl;dr is this:
[MemberInfo]
will report a single group test unless the provided objects for each test can be completely serialized and deserialized by implementingIXunitSerializable
.Background
My own test setup was something like:
The test ran twice, once for each object from
[MemberData]
, as expected. As @NPadrutt experienced, only one item showed up in the Test Explorer, instead of two. This is because the provided objectImpl.Client
was not serializable by either interface xUnit supports (more on this later).In my case, I didn't want to bleed test concerns into my main code. I thought I could write a thin proxy around my real class that would fool the xUnit runner into thinking it could serialize it, but after fighting with it for longer than I'd care to admit, I realized the part I wasn't understanding was:
So any object you provide with
[MemberData]
must support a full round-trip (de-)serialization. This seems obvious to me now, but I couldn't find any documentation on it while I was trying to figure it out.Solution
Make sure every object (and any non-primitive it may contain) can be fully serialized and deserialized. Implementing xUnit's
IXunitSerializable
tells xUnit that it's a serializable object.If, as in my case, you don't want to add attributes to the main code, one solution is to make a thin serializable builder class for testing that can represent everything needed to recreate the actual class. Here's the above code, after I got it to work:
TestClientBuilder
Test
It's mildly annoying that I don't get the target object injected anymore, but it's just one extra line of code to invoke my builder. And, my tests pass (and show up twice!), so I'm not complaining.
MemberData can work with properties or methods which return IEnumerable of object[]. You will see a separate test result for each yield in this scenario:
However, as soon as you will need to pass complex custom objects no matter how many test cases you will have the test output window will show just one test. This is not ideal behaviour and indeed very inconvenient while debugging which test case is failing. The workaround is to create your own wrapper which will derive from IXunitSerializable.
Now you can have your custom objects as parameters to Xunit Theories and still see/debug them as independent results in the test runner window:
Hope this helps.