Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test cases are skipped with TestCaseSource under Visual Studio 2019 #676

Closed
LazarLashev opened this issue Oct 18, 2019 · 69 comments
Closed

Comments

@LazarLashev
Copy link

Running latest versions of Visual Studio 2019 and NUnit (Visual studio 2019 16.3.5, NUnit 3.12.0, NUnit3TestAdapter 3.15.1)

Test with a test source:

[Test]
[TestCaseSource(nameof(TestCases))]
public async Task When_InvalidRequest_ReturnsValidationError(PlayerBalancesRequest request, string siteCode, string correlationToken)
{
   ...
}

public class PlayerBalancesRequest
{
    public long PlayerId { get; set; }
    public string Token { get; set; }
    public string CurrencyCode { get; set; }
}

private static object[] TestCases()
{
    return new object[]
    {
        new object[] { new PlayerBalancesRequest(), null, null },
        new object[] { new PlayerBalancesRequest { PlayerId = 0, Token = GameToken, CurrencyCode = CurrencyCode }, SiteCode, CorrelationToken },
        new object[] { new PlayerBalancesRequest { PlayerId = PlayerId, Token = null, CurrencyCode = CurrencyCode }, SiteCode, CorrelationToken },
        new object[] { new PlayerBalancesRequest { PlayerId = PlayerId, Token = GameToken, CurrencyCode = null}, SiteCode, CorrelationToken },
        ...
    };
}

I have 15 test cases in the test case source. Visual Studio shows all of them in the Test Explorer, but NUnit always runs just 3 of them. Always the same 3, and the rest are skipped. If I remove these 3, then it runs no tests. As a separate issue, if I leave just 1 test case in the test case source, it will think this object[3] that is returned are 3 separate test cases (PlayerBalancesRequest, string and string) and will run the test 3 times with just 1 parameter, failing because the test requires 3 parameters.

@CharliePoole
Copy link
Member

You have only shown four of your test cases, but even with that small number, I can see that three of them will generate tests with the same name. That's the source of your problem.

There's a mismatch between who NUnit works and how TestExplorer wants to see tests. NUnit doesn't care if multiple tests have the same name, because it doesn't use the name to identify the tests. TestExplorer expects each test to have a unique name. The NUnit test adapter does nothing to ensure this. It merely passes on the name provided (usually implicitly) by the user.

In your case, the first argument PlayerBalanceRequest has a default string representation - IIRC <PlayerBalanceRequest>. The second and third args areinsufficient to provide uniqueness. For the four cases you give above, the two names generated will be something like the following, substituting the actual string values for SITECODE and TOKEN.

When_InvalidRequest_ReturnsValidationError(<PlayerBalanceRequest>, null, null)
When_InvalidRequest_ReturnsValidationError(<PlayerBalanceRequest>, "SITECODE", "TOKEN")

Generally, when tests have the same name, TestExplorer groups all the results under the first one found. Select one of the tests that ran and look at the result report, usually directly under the tree but sometimes next to it if your window size is small. I suspect you'll find all fifteen.

So, what can you do about this? There are a two main alternatives...

  1. Override PlayerBalanceRequest.ToString() so the representation includes the values of all the member properties. This will give you unique names for each case, so long as there are no duplicates - I assume you wouldn't want duplicates anyway.

  2. Replace the inner new object[]s with new TestCaseData() and use TestCaseData.SetName to assign a unique name to each case. If you do this, I advise replacing the outer new object[] with new TestCaseData[] because it's much clearer. Personally, I believe that use of TestCaseData is a better practice in general, because it clearly says: "Here is one set of test case data!" and because you can easily add features like ignoring a case, changing the name, providing a description, etc.

Regarding the "separate issue" you mentioned, Did you eliminate the outer object[]? If you did that, it's a user error. 😄 If you retained the nesting, then it might be a bug in the framework. In either case, the problem goes away if you use TestCaseData instead.

@CharliePoole
Copy link
Member

@nunit/framework-team Maybe we should consider reducing the number of options available for use with TestCaseSource for NUnit 4. I'd be happy to actually require TestCaseData items under a source. The only possible exception in my mind is for a method with a single argument. In that case, allowing an array of the declared type to represent the list of args is more terse. However, it opens up problems when the type of the single argument is, in fact, some sort of array! I've often regretted trying to make this so flexible in the first place.

@LazarLashev
Copy link
Author

Thanks for the detailed explanation @CharliePoole! I will try your suggestions and report the results. However, I don't think the issues can be fully explained by this theory. Here are all the test cases:

// NUnit runs these:
new object[] { new PlayerBalancesRequest(), null, null },
new object[] { new PlayerBalancesRequest { PlayerId = PlayerId, Token = GameToken, CurrencyCode = CurrencyCode }, SiteCode, null },
new object[] { new PlayerBalancesRequest { PlayerId = PlayerId, Token = GameToken, CurrencyCode = CurrencyCode }, SiteCode, string.Empty },

// NUnit skips these:
new object[] { new PlayerBalancesRequest { PlayerId = -1, Token = GameToken, CurrencyCode = CurrencyCode }, SiteCode, CorrelationToken },
new object[] { new PlayerBalancesRequest { PlayerId = 0, Token = GameToken, CurrencyCode = CurrencyCode }, SiteCode, CorrelationToken },
new object[] { new PlayerBalancesRequest { PlayerId = PlayerId, Token = null, CurrencyCode = CurrencyCode }, SiteCode, CorrelationToken },
new object[] { new PlayerBalancesRequest { PlayerId = PlayerId, Token = string.Empty, CurrencyCode = CurrencyCode }, SiteCode, CorrelationToken },
new object[] { new PlayerBalancesRequest { PlayerId = PlayerId, Token = new string('*', 51), CurrencyCode = CurrencyCode }, SiteCode, CorrelationToken },
new object[] { new PlayerBalancesRequest { PlayerId = PlayerId, Token = GameToken, CurrencyCode = CurrencyCode }, null, CorrelationToken },
new object[] { new PlayerBalancesRequest { PlayerId = PlayerId, Token = GameToken, CurrencyCode = CurrencyCode }, string.Empty, CorrelationToken },
new object[] { new PlayerBalancesRequest { PlayerId = PlayerId, Token = GameToken, CurrencyCode = CurrencyCode }, new string('*', 51), CorrelationToken }

As you can see there are various test cases where the site code and correlation token are different, but these test cases will still be ignored. I have put the ones NUnit runs in the beginning, but the ordering does not matter; it only ever runs these 3.

For example, if I leave only the last 3 test cases, they will all be ignored and no test cases will be run at all.

new object[] { new PlayerBalancesRequest { PlayerId = PlayerId, Token = GameToken, CurrencyCode = CurrencyCode }, null, CorrelationToken },
new object[] { new PlayerBalancesRequest { PlayerId = PlayerId, Token = GameToken, CurrencyCode = CurrencyCode }, string.Empty, CorrelationToken },
new object[] { new PlayerBalancesRequest { PlayerId = PlayerId, Token = GameToken, CurrencyCode = CurrencyCode }, new string('*', 51), CorrelationToken }

@CharliePoole
Copy link
Member

Hmmm... that messes up my theory all right! What platform do your tests target? If it's .NET Framework, can you check what happens if you run them under the nunit3-console runner?

@mikkelbu
Copy link
Member

Is there any warnings/errors under the output for test execution? And can you upload the example or create a github repository that we can clone, so that we can recreate the problem easily.

@LazarLashev
Copy link
Author

Good morning, here is a small sample project that illustrates the problem:
NUnitTest.zip

If I run the tests through the console runner (in our case through TeamCity) it does run all tests, so the problem is related to the tests adapter.

No errors or warnings in the output window!

@mikkelbu
Copy link
Member

mikkelbu commented Nov 3, 2019

@LazarLashev What is the outcome if you use https://www.myget.org/feed/nunit/package/nuget/NUnit3TestAdapter/3.16.0-dev-01202 instead of NUnit3TestAdapter 3.15.1? (The build contains a rather substantial change to how the adapter identifies test cases, see #668 for a longer - and better - explanation)

@APErebus
Copy link

APErebus commented Nov 6, 2019

I was having a similar issue with VS 2019 & TestCaseSource. The dev 3.16.0 test adapter seems to have resolved the issue.

Test Explorer can now find and run my tests.

@LazarLashev
Copy link
Author

@mikkelbu I have taken the sample test project above (NUnitTest.zip) and ran the tests with the updated adapter 3.16.0-dev-01202. It failed to run the tests, showing this output:

[11/6/2019 9:57:55.396 AM Informational] ---------- Discovery started ----------
[11/6/2019 9:57:55.398 AM Informational] ========== Discovery skipped: All test containers are up to date ==========
[11/6/2019 9:57:55.404 AM Informational] ---------- Run started ----------
[11/6/2019 9:57:55.797 AM Error] System.ArgumentException: An item with the same key has already been added.
   at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at Microsoft.VisualStudio.TestWindow.Client.VirtualTestNodeHierarchy.<>c__DisplayClass40_1.<<RefreshTestsAsync>b__1>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.TestWindow.Utilities.EventPumpExtensions.<>c__DisplayClass3_0.<<EnqueueAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.TestWindow.Extensibility.ILoggerExtensions.<CallWithCatchAsync>d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.VisualStudio.Telemetry.WindowsErrorReporting.WatsonReport.GetClrWatsonExceptionInfo(Exception exceptionObject)
[11/6/2019 9:57:56.108 AM Informational] NUnit Adapter 3.16.0.0: Test execution started
[11/6/2019 9:57:56.123 AM Informational] Running selected tests in D:\Projects\Test\NUnit\bin\Debug\netcoreapp2.2\win-x64\NUnitTest.dll
[11/6/2019 9:57:56.383 AM Informational]    NUnit3TestExecutor converted 11 of 11 NUnit test cases
[11/6/2019 9:57:56.566 AM Informational] NUnit Adapter 3.16.0.0: Test execution complete
[11/6/2019 9:57:56.661 AM Informational] ========== Run finished: 11 tests run (0:00:01.2205531) ==========
[11/6/2019 9:57:56.856 AM Error] System.ArgumentException: An item with the same key has already been added.
   at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at Microsoft.VisualStudio.TestWindow.Client.VirtualTestNodeHierarchy.<>c__DisplayClass40_1.<<RefreshTestsAsync>b__1>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.TestWindow.Utilities.EventPumpExtensions.<>c__DisplayClass3_0.<<EnqueueAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.TestWindow.Extensibility.ILoggerExtensions.<CallWithCatchAsync>d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.VisualStudio.Telemetry.WindowsErrorReporting.WatsonReport.GetClrWatsonExceptionInfo(Exception exceptionObject)

@lobster2012-user
Copy link

lobster2012-user commented Nov 7, 2019

@LazarLashev
Have you tried to completely clean the project? (remove obj \ bin)

изображение

изображение

@LazarLashev
Copy link
Author

I have tried to run the tests after removing bin/obj, and the first time they all passed. The second time I tried to run the tests, I got what it seems like the same error as before

[11/7/2019 5:08:47.041 PM Informational] ---------- Discovery started ----------
[11/7/2019 5:08:47.050 PM Informational] ========== Discovery skipped: All test containers are up to date ==========
[11/7/2019 5:08:47.085 PM Informational] ---------- Run started ----------
[11/7/2019 5:08:47.404 PM Error] System.ArgumentException: An item with the same key has already been added.
   at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at Microsoft.VisualStudio.TestWindow.Client.VirtualTestNodeHierarchy.<>c__DisplayClass40_1.<<RefreshTestsAsync>b__1>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.TestWindow.Utilities.EventPumpExtensions.<>c__DisplayClass3_0.<<EnqueueAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.TestWindow.Extensibility.ILoggerExtensions.<CallWithCatchAsync>d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.VisualStudio.Telemetry.WindowsErrorReporting.WatsonReport.GetClrWatsonExceptionInfo(Exception exceptionObject)
[11/7/2019 5:08:47.766 PM Informational] NUnit Adapter 3.16.0.0: Test execution started
[11/7/2019 5:08:47.785 PM Informational] Running selected tests in D:\Projects\Test\NUnit\bin\Debug\netcoreapp2.2\win-x64\NUnitTest.dll
[11/7/2019 5:08:48.189 PM Informational]    NUnit3TestExecutor converted 11 of 11 NUnit test cases
[11/7/2019 5:08:48.410 PM Informational] NUnit Adapter 3.16.0.0: Test execution complete
[11/7/2019 5:08:48.544 PM Informational] ========== Run finished: 11 tests run (0:00:01.3926949) ==========
[11/7/2019 5:08:48.703 PM Error] System.ArgumentException: An item with the same key has already been added.
   at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at Microsoft.VisualStudio.TestWindow.Client.VirtualTestNodeHierarchy.<>c__DisplayClass40_1.<<RefreshTestsAsync>b__1>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.TestWindow.Utilities.EventPumpExtensions.<>c__DisplayClass3_0.<<EnqueueAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.TestWindow.Extensibility.ILoggerExtensions.<CallWithCatchAsync>d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.VisualStudio.Telemetry.WindowsErrorReporting.WatsonReport.GetClrWatsonExceptionInfo(Exception exceptionObject)

@CharliePoole
Copy link
Member

It appears as if the error is associated with the test window deciding to skip discovery and use information that it has saved. See the message "Discovery skipped: All test containers are up to date."

Immediately after that message, execution begins. NUnit, in the course of execution, performs it's own discovery once again. This should be private, with no results reported to VS, but it's possible that some reporting is happening. That could conceivably lead to the duplicate key error that is being thrown by the Test window.

In any case, this is clearly an error in the adapter only, so I'm transferring the issue to that project. @OsirisTerje does this match anything else you have seen?

@CharliePoole CharliePoole transferred this issue from nunit/nunit Nov 7, 2019
@OsirisTerje
Copy link
Member

@CharliePoole Not directly, but after the last big related fix, I have at least 3 hanging bugs which should have been fixed by the same. I have asked the MS PG about this one in particular since the stack trace looks unknown to the adapter. I am 99.9% sure nothing is being sent out from the discovery phase with the execution, but we do build up a cache, so there can be something there.

I'll work through this one and see if I can catch a trace from it.

@klreeher
Copy link

klreeher commented Dec 3, 2019

I believe I'm also running into this bug. I have an example project with test @ https://github.com/klreeher/nunitbugexample01/

This repo targets netcoreapp3, and my original repo targets v4.6.1. In the v4.6.1 framework repo, I tried the 3.16.0-dev test adapter, with no change.

My specific example test shows a TestCaseSource that uses the following:

        public static IEnumerable TestCases
        {
            get
            {
                yield return new TestCaseData("bob@bob.com").Returns(true); // not skipped
                yield return new TestCaseData(Faker.InternetFaker.Email()).Returns(true); //skipped
                yield return new TestCaseData(Faker.InternetFaker.Domain()).Returns(false); // sometimes not skipped, if I clean and delete my obj/bin files and am very very good???
                yield return new TestCaseData(GetRandomNaughtyString()).Returns(false); //skipped
            }
        }

@mikkelbu
Copy link
Member

mikkelbu commented Dec 3, 2019

Hi @klreeher. I just tried your example project, and it is a different issue than described above. The problem is that the test execution in VS is a two-phase run, one for discovery and one for execution. So when

yield return new TestCaseData(Faker.InternetFaker.Email()).Returns(true); //skipped
yield return new TestCaseData(Faker.InternetFaker.Domain()).Returns(false); // sometimes not skipped, if I clean and delete my obj/bin files and am very very good???
yield return new TestCaseData(GetRandomNaughtyString()).Returns(false); //skipped

is executed during the discovery it will find testcases with certain values, but when you try to execute these tests then the data will not match for the last three elements as these will have a new value. For GetRandomNaughtyString you can either use a seeded randomizer or perhaps use NUnits own random class via TestContext.CurrentContext.Random. For Faker you can probably also provide a seed to make the values the same in the discovery and in the execution.

The issue #97 has more information on the two-phase run.

@CharliePoole
Copy link
Member

Note that issue #97 was closed as fixed but that the fix assumes you are using NUnit's own randomizer at least as an initial seed. What happens is that the adapter saves the seed used for discovery in a temp file and re-seeds it for execution.

It's a constraint on the adapter that discovery and execution run at different times in two different processes. The only communication possible is through the file system but we must still find or generate the same exact tests in discovery and execution. Currently, we simply save the random seed. A more general approach would be to save and restore the entire tree of discovered tests for reach assembly, but that's kind of a big deal!

@klreeher
Copy link

klreeher commented Dec 3, 2019

@mikkelbu @CharliePoole thank you both for the info! Switching my fake data to use the nunit random seed solved my problem.

@OsirisTerje
Copy link
Member

This bug is a duplicate of #685 .

@OsirisTerje
Copy link
Member

OsirisTerje commented Jan 12, 2020

@LazarLashev This bug works with 3.16.0. In upcoming 3.16.1 you will need to add a runsettingsfile and set the following true: UseParentFQNForParametrizedTests
You might possibly also need to set UseNUnitIdforTestCaseId to true.
Default for both will be false in 3.16.1
When it is released documentation will be found here: https://github.com/nunit/docs/wiki/Tips-And-Tricks

The duplicate key issue is a known issue in VS 16.3 and 16.4. As far as we know, it will be fixed in 16.5

@OsirisTerje
Copy link
Member

This issue is resolved by Version 3.16.1 which is released now on nuget.org and VSIX on Visual Studio marketplace. See all issues resolved and Release notes for details.

@troyxli
Copy link

troyxli commented Feb 8, 2020

@OsirisTerje , could you please share an example of runsetting file of how to set these two config values to true: UseParentFQNForParametrizedTests and UseNUnitIdforTestCaseId to true?

@Arturace
Copy link

Arturace commented Feb 26, 2020

Either the bug still exists or my .runsettings is invalid, because my test cases still get skipped when I run all tests, run tests on the parent node or even directly run the test case:
image

It clearly sees the custom names and puts them under the function decorated with the TestCaseSource attribute, but doesn't run them.
I have the 3.16.1 nuget version of NUnit. VS2019 16.4.5.

Here is my .runsettings (simplified):

<RunSettings>
  <RunConfiguration>
    ...
    <NUnit>
      <UseNUnitIdforTestCaseId>True</UseNUnitIdforTestCaseId>
      <UseParentFQNForParametrizedTests>True</UseParentFQNForParametrizedTests>
    </NUnit>
     ...
</RunSettings>

Update:
The tests seem to be running if I check the output from the tests in VS:

[2020-02-26 11:20:58.536 ] ========== Discovery finished: 189 tests found (0:00:02,4627477) ==========
[2020-02-26 11:28:07.321 ] ---------- Run started ----------
NUnit Adapter 3.16.1.0: Test execution started
Running selected tests in TEST_PRGRAMM_DLL_PATH
   NUnit3TestExecutor converted 4 of 4 NUnit test cases
NUnit Adapter 3.16.1.0: Test execution complete
[2020-02-26 11:28:09.517 ] ========== Run finished: 4 tests run (0:00:02,175586) ==========

It was supposed to be running 4 tests and did run 4 tests. However, the interface of the test explorer doesn't reflect that at all:
image


Another Update:
After the last update, I restarted VS and the Test Explorer now contains the previously "visually unrun" tests under a Unkown Project node (the rest of the tree is same as the project). Those tests are marked as passed. So there definitely seems to be an issue.


And another:
After building, sometimes after collapsing and trying to reopen a node containing the test cases, I get a Loading... and the output screaming System.ArgumentException: An item with the same key has already been added. many times.

@OsirisTerje
Copy link
Member

OsirisTerje commented Apr 18, 2021

Yes, the use of `

TestContext.CurrentContext.Random.NextGuid()

also yields usable guids.
I've pushed up code to the repo showing that too.
image

@gsonnenf
Copy link

Having the same issue with randomly generate TestCaseSource (e.g. Fixture.Create() ) being reported as 'not run' when running them using the MS Test Runner. They are reported as running just fine with the Resharper test runner.

@OsirisTerje
Copy link
Member

@gsonnenf Can you provide a small repro for this?

@gsonnenf
Copy link

@CharliePoole
Copy link
Member

The example uses the .NET Random class to generate tests. It's a known limitation that NUnit's wrapper class, which is part of TestContext, must be used when running under Visual Studio. The adapter contains code to save the random seed used in discovery and re-use it in the execution phase.

@gsonnenf
Copy link

gsonnenf commented Jan 30, 2022

The work around is to use Resharper Test Runner so I don't have add to add additional code to use AutoFixture. I just added the random in there to demonstrate it was not specific to AutoFixture.

But... if its a known issue, that can't be fixed, why ask for me to spend a half hour setting up a test case? -_-

@OsirisTerje
Copy link
Member

OsirisTerje commented Jan 30, 2022

@gsonnenf Sorry about that, but I assumed you had a case that was different from what was discussed above [your comment] - which talks about using the NUnit Random class. If you just added it in the example, and it happens in other places (Autofixture?), then that would be interesting. Without a code example it is very hard for us to understand what you're seeing. I've been using Autofixture myself without seeing any such issues, so it might be something new.

@gsonnenf
Copy link

@OsirisTerje Well, the repo is posted, along with the pictures of what I'm seeing. The issues occurs with value types and strings. It doesn't appear to occur with objects, which is likely what you are using.

@OsirisTerje
Copy link
Member

@gsonnenf Thanks! I'll have a look at it :-)

@OsirisTerje
Copy link
Member

OsirisTerje commented Jan 30, 2022

@gsonnenf There is something strange going on here. I'll create a separate issue for this, and try to debug what this is. This doesn't look like the standard random issue.
Or, actually, it is - but I think we might be able to work around these......

@moh-hassan
Copy link

moh-hassan commented Jan 31, 2022

I have the same problem when using TestCaseSource in vs2019 / vs2022 in TEST EXPLORER, when testcase include GUID
But no problem if I run the test from the terminal using the command dotnet test
and no problem when Run test using Resharper.
I think the problem is related to Test Explorer.

I use the next packages:

<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="FluentAssertions" Version="5.10.0" />

Sample project to show the issue with Guid in Test Explorere vs 2019
GuidTest.zip

Hi @OsirisTerje
I attached a sample project to show the issue.

@moh-hassan
Copy link

moh-hassan commented Jan 31, 2022

I did a workaround to fix the issue by using constant Guid values within TestCaseSource, like this

 private static Guid guid0 = new Guid("005bfef4-ff3d-4898-9b7d-cc6aab8db0eb");        
 private static Guid guid1= new Guid("8bfb35f0-9cad-4e19-ba01-cc09d7d47351");

//Then use these values in TestCaseSource
//avoid using Guid.NewGuid()  in TestCaseSource

Any trial to use Guid.NewGuid() in TestCaseSource, disable test case running.

Update:
it's only disabled in vs2019 Test Explorer.
In Resharper it's working fine.
In terminal, it's run, but with using guid "00000000-0000-0000-0000-000000000000" !!!, which is not usable test case.

It seems it's a known issue since 2016
Related issue

@gsonnenf
Copy link

gsonnenf commented Jan 31, 2022

It appears the random inputs issue might be a problem with the MS Test runner, that can't be fixed from the NUnit side. Perhaps someone should ask them to fix it so their VS built in tool can handle random inputs.

@OsirisTerje
Copy link
Member

OsirisTerje commented Jan 31, 2022

Do you mean this also happens with the MSTest framework?
It may also be dependent upon the use of the Real Time Test discovery feature.

There is anyway a problem with the NUnit system, since it does two passes over the code, one for discovery and then for the real execution. The random values will be different for these two passes. It may be possible to skip the first phase, but that may require the real time discovery to be on, so that we are sure the tests are actually being passed down.

@gsonnenf
Copy link

gsonnenf commented Jan 31, 2022

Ah! My mistake if that's where the issues lies. I may have incorrectly garnered from the entirety of the thread that the issue was elsewhere. (Particularly because its not an issue with the the Resharper test tool.)

@moh-hassan
Copy link

moh-hassan commented Feb 1, 2022

I solved my issue using runsettings as described below:
Create a runsettings in the project root e.g nunit.runsettings with the next minimum configuration:

<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
   <NUnit>
     <UseParentFQNForParametrizedTests>True</UseParentFQNForParametrizedTests>
     <UseNUnitIdforTestCaseId>True</UseNUnitIdforTestCaseId>
   </NUnit>  
</RunSettings>

Add the next line to the PropertyGroup of your project:

<RunSettingsFilePath>$(MSBuildProjectDirectory)\nunit.runsettings</RunSettingsFilePath>

@moh-hassan
Copy link

moh-hassan commented Feb 1, 2022

@gsonnenf
you can resolve your issue by adding a runsettings file as described

The output of test Explorer is as shown below:
gstc
All 9 test cases are passed.

The modified project:
Gstc.Nunit.IssueDemo.zip

@jagdipafdb
Copy link

Yes, the use of `

TestContext.CurrentContext.Random.NextGuid()

also yields usable guids. I've pushed up code to the repo showing that too. image

This was the answer for me - and I think it needs to be highlighted more.

@CharliePoole
Copy link
Member

@jagdipafdb It's fairly well described in the nunit framework docs...
https://docs.nunit.org/articles/nunit/writing-tests/Randomizer-Methods.html

Unfortunately, you need to know it's what you are looking for in order to find it. :-)

@Woudjee
Copy link

Woudjee commented Sep 24, 2024

I believe I have the same problem:

image

image

image

Note that I have multiple test cases, but they are all skipped. I actually have about 500 unit tests I do like this, but this particular test case simply won't run. The crazy thing is that before the changes in my current branch, the code above simply worked, as well as for all my other unit tests. I reviewed all my changes extremely thoroughly, and there is nothing indicating any effect on this setup, including projecting the knowledge from this discussion onto the changes.

Initially, I was not using any runsettings (I have no experience with this). It is not clear to me if I did this correctly:

image

image

I have now (honestly) wasted about 2 hours on this problem and I'm a little fed up, since it was working before and I'd rather spend my time on something else. Any suggestions?


PS. About the changes in my current branch. They do not affect the models/model creation of this Face object or any of its child objects.

@CharliePoole
Copy link
Member

@Woudjee One minor suggestion... NUnit framework sometimes gets confused when the arguments are passed as an object[], even though that's still supported for legacy reasons. Try changing your source to return a TestCaseData[] instead. TestCaseData is the class designed precisely for the purpose of removing the ambiguity which arises when an object[] appears in a position where it might be the argument itself or an array of arguments.

Anyway, this may not be the solution, but it's what came to mind looking at your code.

@Woudjee
Copy link

Woudjee commented Sep 25, 2024

@CharliePoole thank you for you quick reply. I have changed the object[] to TestCaseData[]. I must say that I like this syntax significantly more than the object[]. but unfortunately this did not fix the problem.

What is happening on the background? Because there is no error message or any information why the test won't run.

This does work (thereby clearly indicating that the objects are instantiated correctly):

image

However, the code above defeats the purpose of the TestCaseSource setup.

@mikkelbu
Copy link
Member

@Woudjee Does it work if you set the name of the testcase via SetName on TestCaseData to something fixed?

Are there any useful information in the Output window under "Tests"?
image

@Woudjee
Copy link

Woudjee commented Sep 26, 2024

@mikkelbu I am unsure what you mean by the SetName since I cannot call any methods in this kind of setup:

image

And unfortunately, there is no information there. I have also ran builds and tests with 'verbose', but also here there was no information whatsoever.

@mikkelbu
Copy link
Member

@Woudjee You can call the method on the instance returned by the constructor - so something like this

        internal static TestCaseData[] GetTestCaseData =
        {
            new TestCaseData("Some data", 2).SetName("Test1"),
            new TestCaseData("Another data", 124).SetName("Test2"),
        };

@Woudjee
Copy link

Woudjee commented Sep 26, 2024

@mikkelbu aha, I didn't check the return type and assumed it was void. I tried your suggestion, but unfortunately it didn't work :(

@Woudjee
Copy link

Woudjee commented Oct 3, 2024

So I just tried to make a pull request, and apparantly my pipeline fails. This is more information than I had before!

image

The thing is, I am requesting three arguments:
image

And that I am providing three arguments:
image

Any help is very much appreciated!


Update: I have two tests that fail in the same fashion, I just noticed that I had screenshotted the wrong failing test. But the core information is identical:

image

image

@Woudjee
Copy link

Woudjee commented Oct 3, 2024

So, apparantly, I did make a small mistake. However, also apparantly, my test does run in the context of my pipeline, whereas it does not in the context of the VS test explorer. Why is this? (Quite a funny way to figure this out like this actually)

image

@OsirisTerje
Copy link
Member

OsirisTerje commented Oct 3, 2024

my test does run in the context of my pipeline, whereas it does not in the context of the VS test explorer.

What do you mean?

Also, you're commenting on a closed issue. You can move this into either Discussions, if you just want some help along the way, or if you really believe this is kind of the same issue, you should raise a new one (you can point back to this), and if it is a regression, you can point that out too, or if you mean this is a variation, state that :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests