Mvvmcross Testing different view models fails when running together - xamarin

I've come across an interesting error. I have two test files for my xamarin mobile application, both testing view models:
public class TestFirstViewModel : MvxIoCSupportingTest
{
public void AdditionalSetup() {
//Register services and dependencies here.
}
[Fact]
public TestMethod1() {
// Successful test code here.
}
}
That's in one file. In another file, I have:
public class TestSecondViewModel : MvxIoCSupportingTest
{
public void AdditionalSetup() {
//Register services and dependencies here, slightly different from first
}
[Fact]
public TestMethod2() {
// Successful test code here.
}
}
When I run these files individually (I'm using xunit), they work just fine. However, when I run them together, I get the following error on one of the test cases:
Result Message: Cirrious.CrossCore.Exceptions.MvxException : You cannot create more than one instance of MvxSingleton
Result StackTrace:
at Cirrious.CrossCore.Core.MvxSingleton`1..ctor()
at Cirrious.CrossCore.IoC.MvxSimpleIoCContainer..ctor(IMvxIocOptions options)
at Cirrious.CrossCore.IoC.MvxSimpleIoCContainer.Initialize(IMvxIocOptions options)
at Cirrious.MvvmCross.Test.Core.MvxIoCSupportingTest.ClearAll()
at Cirrious.MvvmCross.Test.Core.MvxIoCSupportingTest.Setup()
at Project.Test.TestFirstViewModel.TestMethod1() in ...
Can anyone tell me what's going on here?

The issue stems from the parallelization of XUnit without the option to do proper tear-down. You could diable parallelization in the AssemblyIndo.cs file in you test project by adding:
[assembly: CollectionBehavior(DisableTestParallelization = true)]

I ended up solving this question by changing testing frameworks. I had different ioc singleton initializations, because, well, they're different test cases and needed different inputs/mocks. Instead of using Xunit, I resorted to Nunit where their cache clearing was much more defined: Xunit doesn't exactly believe in setup and tear-down, so it made a test environment like this more difficult.

I fixed the issue by using the collection attribute.
[Collection("ViewModels")]
class ViewModelATest : BaseViewModelTest {
...
}
[Collection("ViewModels")]
class ViewModelBTest : BaseViewModelTest {
...
}
The base view model test class has the mock dispatcher and performs the singleton registrations in the additional setup method.
Each of my tests calls ClearAll() at the beginning.

I hade some success with setup things in a constructor and add this check:
public PaymentRepositoryTests()
{
if (MvxSingletonCache.Instance == null)
{
Setup();
}
//other registerings.
}`
Also I did implement the IDisposable Interface
public void Dispose()
{
ClearAll();
}
But tbh not sure how much impact that had..

It works ok with xunit
Copy MvxIocSupportingTest and Mvxtest in your xunit PCL project.
Modify MvxTest to remove the attributes and use a simple contructor:
public class MvxTest : MvxIoCSupportingTest
{
protected MockMvxViewDispatcher MockDispatcher { get; private set; }
public MvxTest()
{
Setup();
}
...
And in each of you test, derive from IClassFixture
public class TestRadiosApi : IClassFixture<MvxTest>
{
[Fact]
public async Task TestToken()
{
...
xunit will create the MvxTest class only once for all tests.

Related

Error if the [AssemblyInitialize] already exists in the test project with Specflow

I've updated Specflow from the 3.0.225 to the 3.1.62 and I received the error Tests_Integration_MSTestAssemblyHooks: Cannot define more than one method with the AssemblyInitialize attribute inside an assembly.
The reason is obviously that I'd had the [AssemblyInitialize] attribute in my project already. How can I fix it?
The reason is that Specflow generates another file in the background which has the AssemblyInitialize/AssemblyCleanup hooks defined. In order to fix that one should use the hooks provided by Specflow, namely BeforeTestRun/AfterTestRun. Like this:
[Binding] // add the Binding attribute on the class with the assembly level hooks
public abstract class SeleniumTest
{
// it used to be [AssemblyInitialize]
[BeforeTestRun]
public static void AssemblyInitialize(/* note there is no TestContext parameter anymore */)
{
// ...
}
// it used to be [AssemblyCleanup]
[AfterTestRun]
public static void AssemblyCleanup()
{
// ...
}
}

Xunit Test for ASP.NET Boilerplate

For ASP.NET Boilerplate, I am trying to implement Xunit test for the project. Is there any method to provide the parameters to test methods and configure the test methods process order.
In the normal test project, I could use [InlineData("Test", 3)] to provide the value to test methods public async void AllInOne_Test(string userName, int count), but for ASP.NET Boilerplate, it did not pass the value to test method.
I also want to process TestB after TestB, how could I achieve this?
Update1:
For InlineData issue, I could inherit TheoryAttribute like below:
public sealed class MultiTenantTheoryAttribute : TheoryAttribute
{
public MultiTenantTheoryAttribute()
{
if (!CloudConsts.MultiTenancyEnabled)
{
Skip = "MultiTenancy is disabled.";
}
}
}
And then, use it like below:
[MultiTenantTheory]
[InlineData("Test_CreateCoupon")]
public async Task CreateCoupon_Test(string title)
{
//....
}
But, I still have an issue with test method processing order.
[MultiTenantTheory]
[InlineData("Test_CreateCoupon")]
public async Task CreateCoupon_Test(string title)
{
//...
}
[MultiTenantTheory]
[InlineData("Test_CreateCoupon", "Test_UpdateCoupon")]
public async Task UpdateCoupon_Test(string oldTitle, string updateTitle)
{
//。。。
}
In my tests, I have CreateCoupon_Test and UpdateCoupon_Test, I want to exec CreateCoupon_Test, then UpdateCoupon_Test when I run all tests. But, currently, its order is random, and I got UpdateCoupon_Test run before CreateCoupon_Test, then my tests failed, since I update the record which has not been created.

Automapper in MVC Core 2.0 services.AddAutoMapper() behavior

I have a solution like this:
MVC Core 2.0 application <-> Business Class library <-> Domain class library
(ViewModel) <- P1 -> (Dto) <-P2-> (Domain entity)
I created Automapper profiles in each MVC and Business projects for mapping ViewModel<->Dto (P1) and Dto<->Domain entity (P2). P1 profile&map is in MVC project, P2 profile&map is in Business library.
I then made a xUnit test project which creates a Dto object and sends it to a Business Service, inside the unit test on init I call:
Business.App.AutoMapperConfiguration.Configure();
And this unit test works exactly as expected.
I then do the same (I even copy/pasted code from Unit test) in the MVC controller and I get an error in mapping Dto to Domain entity:
Unmapped members were found. Review the types and members below...
I configured Automapper maps in startup.cs like this:
services.AddAutoMapper();
If I understand correctly this is supposed to traverse all assemblies for classes inheriting Profile and adding them to configuration.
Example map:
public class StrankaMap : Profile
{
public override string ProfileName => nameof(StrankaMap);
public StrankaMap()
{
CreateMap<SomeDto, SomeDomainEntity>().ReverseMap()
CreateMap<AnotherDto, AnotherDomainEntity>().ReverseMap()
}
}
I don't know what is the cause of this error if my unit test works but not from MVC app - I even copied the code from unit test to MVC controller and ran that. I'm suspecting an error in configuration. Do I assume correctly that inside Startup.cs adding services.AddAutoMapper(); is enough for this to work?
Solution (edit)
Apparently I misunderstood that the service.AddAutoMapper() will traverse all assemblies and search for Profile inherited classes. There might be a better solution but I used the one below, with the help of a hint from the comment #LucianBargaoanu.
I solved it like this:
// Startup.cs
services.AddAutoMapper(
typeof(Business.App.AutoMapperConfiguration),
typeof(MvcApp.Infrastructure.Configuration.AutoMapperConfiguration));
//And the AutoMapperConfiguration class:
namespace MvcApp.Infrastructure.Configuration
{
using AutoMapper;
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<Models.Mapping.StrankaMap>();
});
}
}
}
Apparently I misunderstood that the service.AddAutoMapper() will traverse all assemblies and search for Profile inherited classes. There might be a better solution but I used the one below, with the help of a hint from the comment #LucianBargaoanu.
I solved it like this:
// Startup.cs
services.AddAutoMapper(
typeof(Business.App.AutoMapperConfiguration),
typeof(MvcApp.Infrastructure.Configuration.AutoMapperConfiguration));
//And the AutoMapperConfiguration class:
namespace MvcApp.Infrastructure.Configuration
{
using AutoMapper;
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<Models.Mapping.StrankaMap>();
});
}
}
}

ninject 3 render out object context instances

Entity Framework 4, Ninject 3, MVC3
Currently in my web app i have been using a rather rudimentary approach to per request instantiation of an Object Context. So I am experimenting with Ninject, and some old sample code, but I am unsure how to proceed with the following..
Effectively I want to be able in the controller to do the equivalent of: DB_Entities.Current.Albums ... Should i be instantiating a StandardKernel every time?
The sample i was looking at was using the following: MvcApplication.Container.Get(); but in Ninject 3 with the App_Start hookup I dont have access to Container..
My attempt to replicate the above line, is failing at runtime.
using MusicStoreEntities;
using Ninject;
using TestMVC3WithIOC.App_Start;
using System.Data.Objects;
namespace TestMVC3WithIOC.Models
{
public partial class MusicStoreEntities
{
public static MusicStoreEntities Current
{
get
{
using (IKernel kernel = new StandardKernel())
{
return (MusicStoreEntities)kernel.Get<ObjectContext>();
}
}
}
}
}
Also, note, that in App_Start\NinjectWebCommon.cs I have the following modification:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ILogger>().To<NLogger>();
kernel.Bind<ObjectContext>().To<MusicStoreEntities>().InRequestScope();
}
Although a workable solution, it seems ill-advised to pass the entire Kernel into a class, because it tends to obscure the classes actual, specific dependencies. A better approach is to pass a factory dependency into your controller's constructor.
public partial class MusicStoreEntities
{
private readonly IEntitiesFactory _factory;
public MusicStoreEntities(IEntitiesFactory factory)
{
_factory = factory;
}
}
IEntitiesFactory has a simple implementation with a single method GetObjectContext().
(I believe also the "Unit of Work" pattern is popular at the moment, but I can't really speak to that as I haven't used it. Maybe worth looking into.)

Forcing MSTest to use a single thread

Given this test fixture:
[TestClass]
public class MSTestThreads
{
[TestMethod]
public void Test1()
{
Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
[TestMethod]
public void Test2()
{
Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
}
Running the test with MSTest through Visual Studio or command line prints two different thread numbers (yet they are run sequentially anyway).
Is there a way to force MSTest to run them using a single thread?
I solved this problem with locking:
public static class IntegrationTestsSynchronization
{
public static readonly object LockObject = new object();
}
[TestClass]
public class ATestCaseClass
{
[TestInitialize]
public void TestInitialize()
{
Monitor.Enter(IntegrationTestsSynchronization.LockObject);
}
[TestCleanup]
public void TestCleanup()
{
Monitor.Exit(IntegrationTestsSynchronization.LockObject);
}
//test methods
}
// possibly other test cases
This can of course be extracted to a base test class and reused.
I've fought for endless hours to make MSTest run in a single threaded mode on a large project that made heavy use of nHibernate and it's not-thread-safe (not a problem, it's just not) ISession.
We ended up more time writing code to support the multi-threaded nature of MSTest because - to the best of my and my teams knowledge - it is not possible to run MSTest in a single threaded mode.
You can derive your test class from
public class LinearTest
{
private static readonly object SyncRoot = new object();
[TestInitialize]
public void Initialize()
{
Monitor.Enter(SyncRoot);
}
[TestCleanup]
public void Cleanup()
{
Monitor.Exit(SyncRoot);
}
}
We try hard to make out tests isolated from each other. Many of them achieve this by setting up the state of a database, then restoring it afterwards. Although mostly tests set up different data, with some 10,000 in a run there is a fair chance of a collision unless the code author of a test takes care to ensure its initial data is unique (ie doesn't use the same primary keys as another test doing something similar). This is, frankly, unmanageable, and we do get occasional test failures that pass second time around. I am fairly sure this is caused by collisions that would be avoided running tests strictly sequentially.
The way to make an MSTest method run in single-threaded mode:
Nuget:
install-package MSTest.TestAdapter
install-package MSTest.TestFramework
In your test source on those methods that need to run while no other tests are running:
[TestMethod]
[DoNotParallelize]
public void myTest(){
//
}
Whilst it is a cop out answer, I would actually encourage you to make your code thread-safe. The behaviour of MSTest is to ensure isolation as Richard has pointed out. By encountering problems with your unit tests you are proving that there could be some problems in the future.
You could ignore them, use NUnit, or deal with them and continue to use MSTest.
I tried a bit of a different approach, because the underlying problem is that the names of the pipes are the problem. So I made a fakePipe, derived it from the one I use in the program. And named the pipe with the tests name.
[TestClass]
public class PipeCommunicationContractTests {
private PipeDummy pipe;
/// <summary>
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///</summary>
public TestContext TestContext { get; set; }
[TestInitialize]
public void TestInitialize() {
pipe = new PipeDummy(TestContext.TestName);
pipe.Start();
}
[TestCleanup]
public void TestCleanup() {
{
pipe.Stop();
pipe = null;
}
...
[TestMethod]
public void CallXxOnPipeExpectResult(){
var result = pipe.Xx();
Assert.AreEqual("Result",result);
}
}
It appears to be a bit faster, since we can run on multiple cores and threads...

Resources