Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jenkins failed unit CanExecute test's methods nondeterministic

We have a lot CanExecute tests for a various commands in our project. All tests passed properly when we use Visual Studio testing or AxoCover.

We tried to add some previous object initialization, before executing 'CanExecute' and sometimes it worked (or we thought that).

testedViewModel.Object.InEditMode = inEditMode;

I have a test:

[TestCase(true, true, TestName = "Command_InEditMode_CanExecute")]
[TestCase(false, false, TestName = "Command_NotInEditMode_CannotExecute")]
public void CommandCanExecute(bool inEditMode, bool expectedResult)
{
    var testedViewModel =
        new Mock<SomeViewModel>(inEditMode)
        {
            CallBase = true
        };

    testedViewModel.Setup(x => x.InEditMode).Returns(inEditMode);

    Assert.AreEqual(expectedResult, testedViewModel.Object.Command.CanExecute(null));
}

Sometimes (NOT ALWAYS) when Jenkins does the build and run unit tests some of can execute tests failed with message:

MESSAGE:
  Expected: True
  But was:  False

+++++++++++++++++++  
STACK TRACE:
   at Project.CommandCanExecute(Boolean inEditMode, Boolean expectedResult)

The problem is that is happening only on Jenkins and it's very nondeterministic.

EDIT:

Ok, one more thing to think about. Property InEditMode is placed in base parent class of SomeModelView.

And I merged code for you in the sample.

public BaseViewModel 
{
    public virtual bool InEditMode {get; set;}
}

public SomeViewModel : BaseViewModel
{
    public SomeViewModel () : base ()
    {

    }

    public ICommand Command { get; set; }

    public virtual void RegisterCommands()
    {
        Command = new RelayCommand(/*Do something*/, () => InEditMode);
    }
}

And we think that can be related, that object is thinking that is initialized before initialization of base class is done. But that is very hard to check this with a Jenkins.


SOLUTION

I've created an attribute class:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Assembly, AllowMultiple = true)]
public class GarbageCollectorDisabler : Attribute, ITestAction
{
    public void BeforeTest(ITest test)
    {
        GC.TryStartNoGCRegion(2048 * 4096);
    }

    public void AfterTest(ITest test)
    {
        GC.EndNoGCRegion();
    }

    public ActionTargets Targets => ActionTargets.Test;
}

And then I can use for each 'CanExecute' test this attribute:

[GarbageCollectorDisabler]
[TestCase(TestName = "SomeTest_InEditMode_CanExecute")]
public void SomeTestCanExecute()
{}
like image 251
Piotr Zieliński Avatar asked Jun 04 '19 12:06

Piotr Zieliński


1 Answers

Smells like a garbage collection issue to me. I don't see anything that jumps out at me in your sample, though the code sample is incomplete (where is RegisterCommands invoked?) so something crucial could be missing.

See the source for RelayCommand.CanExecute(). It takes a weak reference to the action you pass in, and once that action is collected CanExecute will return false. See my answer here for an example of this happening.

I reiterate @Nkosi's comment, create a minimal example rather than showing us bits and pieces.

like image 90
Patrick Quirk Avatar answered Sep 26 '22 06:09

Patrick Quirk