使用 NUnit Test Runner 測試命令和導航的簡單 Specflow
我們為什麼需要這個?
目前在 Xamarin.Forms 中進行單元測試的方法是通過平臺執行,因此你的測試必須在 ios,android,windows 或 mac UI 環境中執行 : 在 IDE 中執行測試
Xamarin 還使用 Xamarin.TestCloud 產品提供了令人敬畏的 UI 測試,但是當想要實現 BDD 開發實踐,並且能夠測試 ViewModel 和 Commands,同時在本地或構建伺服器上的單元測試執行器上廉價執行時,沒有建成的方式。
我開發了一個庫,允許使用 Specflow 和 Xamarin.Forms 輕鬆實現從 Scenarios 定義到 ViewModel 的功能,獨立於用於 App 的任何 MVVM 框架(例如 XLabs , MVVMCross , Prism )
如果你是 BDD 的新手 ,請檢查 Specflow out。
用法:
-
如果你還沒有,請從此處(或從你的 visual studio IDE)安裝 specflow visual studio 擴充套件: https ://visualstudiogallery.msdn.microsoft.com/c74211e7-cb6e-4dfa-855d-df0ad4a37dd6
-
將類庫新增到 Xamarin.Forms 專案中。那是你的測試專案。
-
將 nuget 中的 SpecFlow.Xamarin.Forms 包新增到你的測試專案中。
-
向你繼承
TestApp
的測試專案新增一個類,並註冊你的 views / viewmodels 對以及新增任何 DI 註冊,如下所示:
public class DemoAppTest : TestApp
{
protected override void SetViewModelMapping()
{
TestViewFactory.EnableCache = false;
// register your views / viewmodels below
RegisterView<MainPage, MainViewModel>();
}
protected override void InitialiseContainer()
{
// add any di registration here
// Resolver.Instance.Register<TInterface, TType>();
base.InitialiseContainer();
}
}
- 將 SetupHook 類新增到測試專案中,以便為你新增 Specflow 掛鉤。你將需要按照以下方式引導測試應用程式,提供你在上面建立的類以及你的應用程式初始檢視模型:
[Binding]
public class SetupHooks : TestSetupHooks
{
/// <summary>
/// The before scenario.
/// </summary>
[BeforeScenario]
public void BeforeScenario()
{
// bootstrap test app with your test app and your starting viewmodel
new TestAppBootstrap().RunApplication<DemoAppTest, MainViewModel>();
}
}
- 你需要在你的 xamarin.forms 檢視程式碼隱藏中新增一個 catch 塊,以便忽略 xamarin.forms 框架,迫使你執行 app ui(我們不想做的事情):
public YourView()
{
try
{
InitializeComponent();
}
catch (InvalidOperationException soe)
{
if (!soe.Message.Contains("MUST"))
throw;
}
}
-
為專案新增 specflow 功能(使用 vs specflow 擴充套件附帶的 vs specflow 模板)
-
建立/生成繼承 TestStepBase 的步驟類,將 scenarioContext 引數傳遞給基類。
-
使用導航服務和幫助程式來導航,執行命令和測試檢視模型:
[Binding]
public class GeneralSteps : TestStepBase
{
public GeneralSteps(ScenarioContext scenarioContext)
: base(scenarioContext)
{
// you need to instantiate your steps by passing the scenarioContext to the base
}
[Given(@"I am on the main view")]
public void GivenIAmOnTheMainView()
{
Resolver.Instance.Resolve<INavigationService>().PushAsync<MainViewModel>();
Resolver.Instance.Resolve<INavigationService>().CurrentViewModelType.ShouldEqualType<MainViewModel>();
}
[When(@"I click on the button")]
public void WhenIClickOnTheButton()
{
GetCurrentViewModel<MainViewModel>().GetTextCommand.Execute(null);
}
[Then(@"I can see a Label with text ""(.*)""")]
public void ThenICanSeeALabelWithText(string text)
{
GetCurrentViewModel<MainViewModel>().Text.ShouldEqual(text);
}
}