[英]Unit tests fail on Run All but pass when are run single
I have this test class and when I run these tests one at a time they pass but when I try to run all tests from this class, first test is always passed and others usually fail (sometimes randomly one of them pass) and when I run all tests from my project all of these tests fail. 我有这个测试类,当我一次运行一次这些测试时,但是当我尝试运行该类中的所有测试时,总是会通过第一个测试,而其他测试通常会失败(有时会其中一个通过),而当我运行时我项目中的所有测试全部失败。
I use NUnit and Moq frameworks. 我使用NUnit和Moq框架。
Here is the code: 这是代码:
using System.Security;
using DebtDiary.Core;
using DebtDiary.DataProvider;
using Moq;
using NUnit.Framework;
namespace DebtDiary.Tests.ViewModels
{
[TestFixture]
public class LoginPageViewModelTests
{
[Test]
public void TestLoginCommandCallsLoginUserInClientDataStoreWhenDataIsValid()
{
Mock<IApplicationViewModel> stubApplicationVM = new Mock<IApplicationViewModel>();
Mock<IDiaryPageViewModel> stubDiaryPageVM = new Mock<IDiaryPageViewModel>();
Mock<IDialogFacade> stubDialogFacadeVM = new Mock<IDialogFacade>();
Mock<IClientDataStore> mockClientDataStore = new Mock<IClientDataStore>();
Mock<IDataAccess> stubDataAccess = new Mock<IDataAccess>();
var loginPageVM = new LoginPageViewModel(stubApplicationVM.Object, stubDiaryPageVM.Object, stubDialogFacadeVM.Object, mockClientDataStore.Object, stubDataAccess.Object);
loginPageVM.Username = "test";
Mock<IHavePassword> stubPassword = new Mock<IHavePassword>();
SecureString ss = new SecureString();
ss.AppendChar('t');
stubPassword.Setup(x => x.Password).Returns(ss);
User user = new User();
stubDataAccess.Setup(x => x.UserExist(It.IsAny<string>(), It.IsAny<string>())).Returns(true);
stubDataAccess.Setup(x => x.TryGetUser(It.IsAny<string>(), It.IsAny<string>(), out user)).Returns(true);
loginPageVM.LoginCommand.Execute(stubPassword.Object);
mockClientDataStore.Verify(x => x.LoginUser(It.IsAny<User>()), Times.Once());
}
[Test]
public void TestLoginCommandUpdatesDebtorsListInDiaryPageViewModelWhenDataIsValid()
{
Mock<IApplicationViewModel> stubApplicationVM = new Mock<IApplicationViewModel>();
Mock<IDiaryPageViewModel> mockDiaryPageVM = new Mock<IDiaryPageViewModel>();
Mock<IDialogFacade> stubDialogFacadeVM = new Mock<IDialogFacade>();
Mock<IClientDataStore> stubClientDataStore = new Mock<IClientDataStore>();
Mock<IDataAccess> stubDataAccess = new Mock<IDataAccess>();
var loginPageVM = new LoginPageViewModel(stubApplicationVM.Object, mockDiaryPageVM.Object, stubDialogFacadeVM.Object, stubClientDataStore.Object, stubDataAccess.Object);
loginPageVM.Username = "test";
Mock<IHavePassword> stubPassword = new Mock<IHavePassword>();
SecureString ss = new SecureString();
ss.AppendChar('t');
stubPassword.Setup(x => x.Password).Returns(ss);
User user = new User();
stubDataAccess.Setup(x => x.UserExist(It.IsAny<string>(), It.IsAny<string>())).Returns(true);
stubDataAccess.Setup(x => x.TryGetUser(It.IsAny<string>(), It.IsAny<string>(), out user)).Returns(true);
loginPageVM.LoginCommand.Execute(stubPassword.Object);
mockDiaryPageVM.Verify(x => x.UpdateDebtorsList(), Times.Once());
}
[Test]
public void TestLoginCommandUpdatesUsersDataInDiaryPageViewModelWhenDataIsValid()
{
Mock<IApplicationViewModel> stubApplicationVM = new Mock<IApplicationViewModel>();
Mock<IDiaryPageViewModel> mockDiaryPageVM = new Mock<IDiaryPageViewModel>();
Mock<IDialogFacade> stubDialogFacadeVM = new Mock<IDialogFacade>();
Mock<IClientDataStore> stubClientDataStore = new Mock<IClientDataStore>();
Mock<IDataAccess> stubDataAccess = new Mock<IDataAccess>();
var loginPageVM = new LoginPageViewModel(stubApplicationVM.Object, mockDiaryPageVM.Object, stubDialogFacadeVM.Object, stubClientDataStore.Object, stubDataAccess.Object);
loginPageVM.Username = "test";
Mock<IHavePassword> stubPassword = new Mock<IHavePassword>();
SecureString ss = new SecureString();
ss.AppendChar('t');
stubPassword.Setup(x => x.Password).Returns(ss);
User user = new User();
stubDataAccess.Setup(x => x.UserExist(It.IsAny<string>(), It.IsAny<string>())).Returns(true);
stubDataAccess.Setup(x => x.TryGetUser(It.IsAny<string>(), It.IsAny<string>(), out user)).Returns(true);
loginPageVM.LoginCommand.Execute(stubPassword.Object);
mockDiaryPageVM.Verify(x => x.UpdateUsersData(), Times.Once());
}
[Test]
public void TestLoginCommandResetsCurrentSubpageInApplicationViewModelWhenDataIsValid()
{
Mock<IApplicationViewModel> mockApplicationVM = new Mock<IApplicationViewModel>();
Mock<IDiaryPageViewModel> stubDiaryPageVM = new Mock<IDiaryPageViewModel>();
Mock<IDialogFacade> stubDialogFacadeVM = new Mock<IDialogFacade>();
Mock<IClientDataStore> stubClientDataStore = new Mock<IClientDataStore>();
Mock<IDataAccess> stubDataAccess = new Mock<IDataAccess>();
var loginPageVM = new LoginPageViewModel(mockApplicationVM.Object, stubDiaryPageVM.Object, stubDialogFacadeVM.Object, stubClientDataStore.Object, stubDataAccess.Object);
loginPageVM.Username = "test";
Mock<IHavePassword> stubPassword = new Mock<IHavePassword>();
SecureString ss = new SecureString();
ss.AppendChar('t');
stubPassword.Setup(x => x.Password).Returns(ss);
User user = new User();
stubDataAccess.Setup(x => x.UserExist(It.IsAny<string>(), It.IsAny<string>())).Returns(true);
stubDataAccess.Setup(x => x.TryGetUser(It.IsAny<string>(), It.IsAny<string>(), out user)).Returns(true);
loginPageVM.LoginCommand.Execute(stubPassword.Object);
mockApplicationVM.Verify(x => x.ResetCurrentSubpage(), Times.Once());
}
}
}
Do you know what can be the reason? 你知道是什么原因吗? As you can see I moved all repeatable code to these methods to avoid dependencies and it doesnt work. 如您所见,我将所有可重复的代码移到了这些方法中以避免依赖,并且它不起作用。
Since, as you mentioned, each test is creating it's own data and there aren't any shared objects, there a few likely reasons causing these failures: 正如您提到的那样,由于每个测试都在创建自己的数据,并且没有任何共享对象,因此有一些可能的原因导致这些失败:
Option A 选项A
Your system under test (ie LoginPageViewModel) is reusing its dependencies in a thread-safe way. 被测系统(即LoginPageViewModel)正在以线程安全的方式重用其依赖项。
If that is the case , then LoginPageViewModel is being instantiated with the mock interfaces created on the first test being run on a given thread. 如果是这种情况 ,那么将使用在给定线程上运行的第一个测试中创建的模拟接口实例化LoginPageViewModel。 It would then re-use those dependencies for any other test run on that thread. 然后,它将在那些线程上进行的任何其他测试中重新使用这些依赖项。 This would result in the first test on that thread to pass (so when you run each test individually, they all pass). 这将导致该线程上的第一个测试通过(因此,当您单独运行每个测试时,它们都将通过)。 However, any subsequent tests run on that thread will fail because even though your passing in new mock interfaces for each test, the mock interfaces from the first test on that thread are being re-used. 但是,在该线程上运行的任何后续测试都将失败,因为即使您为每个测试传递了新的模拟接口,该线程上第一个测试的模拟接口也将被重用。
Solutions to this problem would be to: 解决此问题的方法是:
Option B (this seems to be the case after looking further at your code) 选项B (在进一步查看您的代码后,似乎是这种情况)
Your system under test is calling some async code without awaiting the result. 您的被测系统正在调用一些异步代码,而无需等待结果。 This will cause the async code to continue to run while the control is returned to the caller (in this case, the unit test). 这将导致异步代码继续运行,同时将控件返回给调用者(在本例中为单元测试)。 This would then potentially cause race conditions if the assertions in the test are executed before the async code finishes. 如果在异步代码完成之前执行测试中的断言,则可能会导致争用情况。 And these race conditions would be more likely to occur when the code is executing slower due to some load, such as running multiple tests at once. 当由于某些负载(例如一次运行多个测试)而导致代码执行速度变慢时,更可能出现这些竞争条件。
This seems to be the case in the code that you're testing. 在您正在测试的代码中似乎就是这种情况。 The constructor for LoginPageViewModel constructs a RelayParameterizedCommand , passing in an async delegate. LoginPageViewModel的构造函数构造一个RelayParameterizedCommand ,传入一个异步委托。 However, your unit tests then call RelayParameterizedCommand to execute that delegate that was passed in, without awaiting the result. 但是,您的单元测试然后调用RelayParameterizedCommand来执行传入的委托,而无需等待结果。
The solutions for that would be: 解决方案是:
Action<object>
to Func<object, Task>
. 将传递给RelayParameterizedCommand的委托从Action<object>
为Func<object, Task>
。 Then you can, either 那你可以
_action(parameter).GetAwaiter().GetResult();
保持RelayParameterizedCommand上的Execute方法同步,但仍从委托的Task中获取结果: _action(parameter).GetAwaiter().GetResult();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.