Is there a way to do a conditional TearDown in NUnit?
I have a TestFixture which has a need to run cleanup code for just a few tests, and I don't really want to:
Run the TearDown method on every test
Create a private helper method and call it from the tests requiring cleanup if I can avoid it
There isn't unfortunately.
Can you not do the cleanup in the [TestFixtureTearDown] instead, so once all the tests have finished? I guess that depends on whether the cleanup has to be done before the next test runs.
Alternatively, put those tests that require a cleanup in another class/TextFixture together, away from the other tests. Then you can use a TearDown in there which doesn't need to be conditional.
Edit:
One thing I've just thought of, which could be done to achieve the aim though probably isn't actually worth it for this particular need, is that you can extend NUnit - create your own custom attributes which you could handle however you wanted. This is mentioned here. Like I say, I don't think really you should go down that route for this, but is useful to know none-the-less
You can have the main TearDown in a base class:
[TearDown]
public virtual void TearDown()
{
// Tear down things here
}
and then override it in the class where you have the tests that should not run the tear down code:
[TearDown]
public override void TearDown()
{
// By not calling base.TearDown() here you avoid tearing down
}
Extend all you classes with test from BaseTest
public class BaseTest
{
[SetUp]
public void BeforeTest()
{
GetService<NUnitHooksController>().ExecuteBeforeTestHooks(this);
}
[TearDown]
public void AfterTest()
{
GetService<NUnitHooksController>().ExecuteAfterTestHooks(this);
}
}
Use AfterTest and BeforeTest hooks. Works both with and without category.
public class ExampleTest : BaseTest
{
[Test, Category("asdasd")]
public void Test01()
{
...
}
[AfterTest("asdasd")]
public void ExampleHook()
{
...
}
}
public class NUnitHooksController
{
private readonly ILogger _log;
public NUnitHooksController(ILogger log)
{
_log = log;
}
public void ExecuteBeforeTestHooks(object testClass)
{
ExecuteHooks(testClass, typeof(BeforeTestAttribute));
}
public void ExecuteAfterTestHooks(object testClass)
{
ExecuteHooks(testClass, typeof(AfterTestAttribute));
}
private MethodInfo[] GetHookMethods(object currentTestClass, Type attributeType)
{
return currentTestClass
.GetType()
.GetMethods()
.Where(m => m.GetCustomAttributes(attributeType, false).Length > 0)
.ToArray();
}
private void ExecuteHooks(object testClass, Type requiredAttributeType)
{
var hooks = GetHookMethods(testClass, requiredAttributeType);
var testCategories = GetTestCategories();
foreach (var hook in hooks)
{
var allAttributes = hook.GetCustomAttributes(requiredAttributeType, true);
foreach (var attribute in allAttributes)
{
if (!attribute.GetType().IsEquivalentTo(requiredAttributeType))
{
continue;
}
var hookCategories = GetCategoriesFromAttribute(attribute);
// if we do not have specific category on hook
// or we have at least one same category on hook and test
if (!hookCategories.Any() || hookCategories.Intersect(testCategories).Any())
{
ExecuteHookMethod(testClass, hook);
}
}
}
}
private object[] GetTestCategories()
{
return TestContext.CurrentContext.Test.Properties["Category"].ToArray();
}
private void ExecuteHookMethod(object testClass, MethodInfo method)
{
var hookName = method.Name;
_log.Information($"Executing - '{hookName}' hook");
try
{
method.Invoke(testClass, Array.Empty<object>());
}
catch (Exception e)
{
_log.Error($"Executing of - '{hookName}' hook failed - {e}");
}
}
private string[] GetCategoriesFromAttribute(object attribute)
{
if (attribute is BeforeTestAttribute beforeTestAttribute)
{
return beforeTestAttribute.Categories;
}
if (attribute is AfterTestAttribute afterTestAttribute)
{
return afterTestAttribute.Categories;
}
throw new ArgumentException($"{attribute.GetType().FullName} - does not have categories");
}
}
I have solved this using the name of the test:
namespace TestProject
{
public class TestClass
{
// Test without TearDown
[Test]
public void Test1()
{
Assert.Pass("Test1 passed");
}
// Test with TearDown
[Test]
public void Test2()
{
Assert.Pass("Test2 passed");
}
[TearDown]
public void TearDown()
{
// Execute only after Test2
if (TestContext.CurrentContext.Test.Name.Equals(nameof(this.Test2)))
{
// Execute Test2 TearDown...
}
}
}
}
Or if you want to use the full name of Test2 (TestProject.TestClass.Test2) you can replace the line
if (TestContext.CurrentContext.Test.Name.Equals(nameof(this.Test2)))
by
if (TestContext.CurrentContext.Test.FullName.Equals(typeof(TestClass).FullName + "." nameof(this.Test2)))
Related
[ClassInitialize]
public static void BeforeCls(TestContext tc)
{
Console.WriteLine("ClassInitialize - Before Class");
}
[ClassCleanup]
public static void AfterCls()
{
Console.WriteLine("ClassCleanup - After Class");
}
[TestInitialize]
public void BeforeMethod()
{
Console.WriteLine("TestInitialize - Before Method");
}
[TestCleanup]
public void AfterMethod()
{
Console.WriteLine("TestCleanup - After Method");
}
[TestMethod]
public void TestMethod1()
{
Console.WriteLine("Test Method - 1");
}
Class cleanup is never executed in this code.
here is the Output
ClassInitialize - Before Class
TestInitialize - Before Method
Test Method - 1
TestCleanup - After Method
Invested a bit more time and discovered that using this will work within a test class:
[ClassCleanup(InheritanceBehavior.None))]
public static void ClassCleanup()
{
// Your code goes here
}
And using that will work for a derived test class:
[ClassCleanup(InheritanceBehavior.BeforeEachDerivedClass)]
public static void ClassCleanup()
{
// Your code goes here
}
You can read more about it here.
If the solution is not working for you no matter what, you can define IDisposable. The code will look something like this:
[TestClass]
public class DisposableBaseTest : IDisposable
{
// Your initialize methods
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~DisposableUserBaseTest()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// Your cleanup code
Console.WriteLine("ClassCleanup - After Class");
}
}
}
P.S. And according to carlin.scott from this post, if you receive an exception during ClassInitialize, you want to handle this separately.
ClassCleanup is executed - put a breakpoint inside your method and verify it yourself. Problem is, that this method is executed after all of your tests are completed, so you cannot write message from that method in test output report. This is similar to ClassInitialize method. If you have multiple tests in your file, your ClassInitialize will write a message to only one of them.
I wanna test my Presenter
public class MainPresenter extends MvpBasePresenter<MainView> {
private Repository repository;
private final CompositeDisposable disposables = new CompositeDisposable();
public void setRepository(Repository repository) {
this.repository = repository;
}
public void loadFromRepository() {
getView().showLoading(false);
disposables.add(repository.getCountries()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribeWith(new DisposableObserver<List<Country>>() {
#Override
public void onNext(List<Country> countries) {
if (isViewAttached()) {
getView().setData(countries);
getView().showContent();
}
}
#Override
public void onError(Throwable e) {
if (isViewAttached()) {
getView().showError(e, false);
}
}
#Override
public void onComplete() {
}
}));
}
public void loadFromRemoteDatastore() {
disposables.add(new RemoteDataStore().getCountries()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribeWith(new DisposableObserver<List<Country>>() {
#Override
public void onNext(List<Country> countries) {
if (isViewAttached()) {
getView().setData(countries);
getView().showContent();
}
}
#Override
public void onError(Throwable e) {
if (isViewAttached()) {
getView().showError(e, false);
}
}
#Override
public void onComplete() {
}
}));
}
#Override
public void detachView(boolean retainInstance) {
super.detachView(retainInstance);
if (!retainInstance) {
disposables.clear();
}
}
}
However, I have many doubts, what's the best way to test it
1) Is this alright if I will write these 4 test scenarios
shouldShowContentWhenLoadFromRepository()
shouldShowErrorWhenLoadFromRepository()
shouldShowContentWhenLoadFromRemoteDatastore()
shouldShowErrorWhenLoadFromRemoteDatastore()
2) Should I write a test for detachView(boolean retainInstance) and clear disposables
3) What kind of mechanisms are the best in my case to test RxJava?
Those test scenarios seem reasonable.
It's often good practice to have tests covering an object's public surface, but testing that detachView() clears disposables may be tricky given the current implementation of MainPresenter.
You could create a stub for Repository the returns an Observable returned when GetCountries() is called. You can create both successful and unsuccessful Observables by using Observable.return() & Observable.error() respectively.
If you need more control over the specific timing of the asynchrony the TestScheduler makes it very simple (seen
here).
I've got a library that I'm trying to unit test. I use TraceSource to write out warning details, and as I have the unit tests set to run as part of the build, I'd like them to show up when the tests are run - particularly on failure.
I've tried the following code to register a trace listener, but my WriteLine stuff never gets called, despite the constructor being called. See code at the bottom [1].
How do I get my traces to show up as part of the test runner?
[1] Code for my testcontexttracelistener:
[TestClass]
public class TestContextTraceListener : TraceListener
{
TestContext testContext;
public TestContextTraceListener(TestContext testContext)
{
this.testContext = testContext;
}
public override void Write(string message)
{
// EP TODO: This is likely going to create newlines where they shouldn't be, but testContext doesn't have a .Write, and I'm too lazy to buffer.
this.WriteLine(message);
}
public override void WriteLine(string message)
{
this.testContext.WriteLine(message);
}
}
[TestClass]
public class TestAssemblyInitializeAndCleanup
{
static TraceListener traceListener;
[AssemblyInitialize]
public static void OnInitialize(TestContext testContext)
{
if (traceListener == null)
{
// This code is reached, but the listener never gets triggered.
traceListener = new TestContextTraceListener(testContext);
Trace.Listeners.Add(traceListener);
}
}
[AssemblyCleanup]
public static void OnCleanup()
{
Cleanup();
}
static void Cleanup()
{
if (traceListener != null)
{
traceListener.Flush();
Trace.Listeners.Remove(traceListener);
traceListener.Dispose();
}
}
}
I am trying to find out whether and how it is possible to nest JUnit test runners, e.g. combine a GuiceJUnitRunner, a Parameterized and a HierarchicalcontextRunner.
To me, it seems that JUnit was not designed to achieve this easily, otherwise BlockJUnit4ClassRunner should have a method which passes the next Runner as an argument.
Someone also implemented a ParallelParameterized runner, which looks to me like combining Parallel and Parameterized was not easily possible.
When googling for "nested" and "JUnit", it comes up with lots of information for nested classes, but I'm looking for nesting Runners, not classes.
There is something called NestedRunner for running plain old Java classes in nested configuration.
your test starts with #RunWith(NestedRunner.class) and here is the example that I found:
#RunWith(NestedRunner.class)
public class ListTest {
// inner class for sharing common context
public class WithArrayList {
// some context for these tests
ArrayList<String> list = new ArrayList<String>();
public class WhenEmpty {
#Test
public void itIsEmpty() {
assertTrue(list.isEmpty());
}
public class AfterAddingAnElement {
// some more context for these tests
String element = "Element";
// you can use instance initializer to initialize your context
// it will be run once per test
{
// the list is still empty in here
assertTrue(list.isEmpty());
list.add(element);
}
#Test
public void itIsNotEmpty() {
assertFalse(list.isEmpty());
}
#Test
public void itContainsTheElement() {
assertTrue(list.contains(element));
}
#Test
public void addingAnotherElementIncreasesSize() {
int sizeBeforeAdding = list.size();
list.add("AnotherElement");
assertThat(list.size(), is(greaterThan(sizeBeforeAdding)));
}
#Test
public void listSizeIsStillOne() {
assertThat(list.size(), is(equalTo(1)));
}
}
#Test
public void isStillEmpty() {
assertTrue(list.isEmpty());
}
}
public class WithTwoElements {
#Before
public void init() {
list.add("Element1");
list.add("Element2");
}
#Test
public void hasSizeOfTwo() {
assertThat(list.size(), is(equalTo(2)));
}
}
}
}
and here is the source for further info
I am setting up some MSTest based unit tests. To make my life easier I want to use a base class that handles the generic setup and taredown all of my tests require. My base class looks like this:
[TestClass]
public class DBTestBase {
public TestContext TestContext { get; set; }
[ClassInitialize()]
public static void MyClassInitialize(TestContext testContext) {
var config = new XmlConfigurationSource("ARconfig_test.xml");
ActiveRecordStarter.Initialize(Assembly.Load("LocalModels"), config);
}
[TestInitialize()]
public void MyTestInitialize() {
ActiveRecordStarter.CreateSchema();
Before_each_test();
}
protected virtual void Before_each_test() { }
[TestCleanup()]
public void MyTestCleanup() {
After_each_test();
}
protected virtual void After_each_test() { }
}
My actual test class looks like this:
[TestClass]
public class question_tests : DBTestBase {
private void CreateInitialData() {
var question = new Question()
{
Name = "Test Question",
Description = "This is a simple test question"
};
question.Create();
}
protected override void Before_each_test() {
base.Before_each_test();
CreateInitialData();
}
[TestMethod]
public void test_fetching() {
var q = Question.FindAll();
Assert.AreEqual("Test Question", q[0].Name, "Incorrect name.");
}
}
The TestInitialize function works as expected. But the ClassInitialize function never runs. It does run if I add the following to my child class:
[ClassInitialize()]
public static void t(TestContext testContext) {
MyClassInitialize(testContext);
}
Is it possible to get my base class initialize function to run without referencing it in my child class?
ClassInitialize method is executed if and only if the concerned "class" contains at least one TestMethod, and at least one TestMethod from the class is selected for execution.
Confirm this was a problem for me too. I used a constructor on the base and a destructor for the cleanup
[TestClass]
public class question_tests : DBTestBase {
...
[TestCleanup()]
public void TestCleanup()
{
base.MyTestCleanup();
}