Friday 2 September 2022

Unit Test

Factor MS Test NUnit XUnit Mark test class [TestClass] [TestFixture] N.A. Mark test method [TestMethod] [Test] [Fact] Mark initialization method [TestInitialize] [Setup] N.A. (usually, constructor of the test class is used for initialization) For Data Driven Tests Mark data driven test method [DataTestMethod] N.A. [Theory] Provide data to the method through parameters [DataRow( _ , _)] [TestCase( _ , _)] [InlineData( _ , _)] Some of the frequently used framework methods Assert.AreEqual() Assert.AreEqual() Assert.Equal() Assert.IsNotNull() Assert.IsNotNull() Assert.NotNull() Examples (templates) MS Test using System; using System.Threading.Tasks; using SampleApplication.OrderAdmin.Core.Services.OrderCreation; using Moq; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SampleApplication.OrderAdmin.Core.Tests.Services.OrderCreation { [TestClass] public class OrderCreationServiceTests { [TestInitialize] public void TestInitialize() { } [TestMethod] public async Task SampleTestMethod1() { } [DataTestMethod] [DataRow(3, 5, Membership.Basic)] [DataRow(0, 4, Membership.Basic)] [DataRow(8, 5, Membership.Premium)] [DataRow(5, 4, Membership.Premium)] public void SampleTestMethod2(double sampleparameter1, int sampleparameter2, Membership sampleparameter3) { } } } Nunit using System; using System.Threading.Tasks; using SampleApplication.OrderAdmin.Core.Services.OrderCreation; using Moq; using NUnit.Framework; namespace SampleApplication.OrderAdmin.Core.Tests.Services.OrderCreation { [TestFixture] public class OrderCreationServiceTests { [SetUp] public void TestInitialize() { } [Test] public async Task SampleTestMethod1() { } [TestCase(3, 5, Membership.Basic)] [TestCase(0, 4, Membership.Basic)] [TestCase(8, 5, Membership.Premium)] [TestCase(5, 4, Membership.Premium)] public void SampleTestMethod2(double sampleparameter1, int sampleparameter2, Membership sampleparameter3) { } } } Xunit using System; using System.Threading.Tasks; using WiredBrainCoffee.CupOrderAdmin.Core.Services.OrderCreation; using Moq; using Xunit; namespace WiredBrainCoffee.CupOrderAdmin.Core.Tests.Services.OrderCreation { public class OrderCreationServiceTests { public OrderCreationServiceTests() { } [Fact] public async Task SampleTestMethod1() { } [Theory] [InlineData(3, 5, Membership.Basic)] [InlineData(0, 4, Membership.Basic)] [InlineData(8, 5, Membership.Premium)] [InlineData(5, 4, Membership.Premium)] public void SampleTestMethod2(double sampleparameter1, int sampleparameter2, Membership sampleparameter3) { } } } What do you mean by data-driven testing? What is Data-Driven Testing? Data-driven testing (DDT) is data that is external to your functional tests, and is loaded and used to extend your automated test cases. You can take the same test case and run it with as many different inputs as you like, thus getting better coverage from a single test. How is data-driven testing performed? Data-driven testing is creation of test scripts where test data and/or output values are read from data files instead of using the same hard-coded values each time the test runs. This way, testers can test how the application handles various inputs effectively. It can be any of the below data files. Data-driven testing is a testing methodology where the test case data is separated from the test case logic. You create a series of test scripts that see the same test steps performed repeatedly in the same order, but with a variation of data. NUnit GUI The latest version 2.6.4 of Nunit.exe can download from http://nunit.org/index.php?p=download The installation process barely requires a few minutes once the executable is downloaded. The NUnit graphical user interface looks as in the following after the installation process has completed. Figure 1.1 NUnit GUI After launching the NUnit.exe GUI, it is time to open a project in the form of a DLL or EXE file on which all the unit test cases executed. For that purpose go to the File menu and select Open Project, now choose the Test case DLL or EXE file, and Unit Test case process is ready to execute as described in the following. NOTE NUnit Software will only open the assembly (DLL or EXE) that are developed under Test Driven Project Methodology. Figure 1.2 NUnit Project Loading NUnit Testing Framework NUnit is a unit-testing framework for .NET applications in which the entire application is isolated into diverse modules. Each module is tested independently to ensure that the objective is met. The NUnit Framework caters to a range of attributes that are used during unit tests. They are used to define Test -Fixtures, Test methods, ExpectedException, and Ignore methods. TextFixture Attribute The TestFixture attribute is an indication that a class contains test methods. When you mention this attribute to a class in your project, the Test Runner application will scan it for test methods. The following code illustrates the usage of this attribute as in the following using System; using NUnit.Framework; using System.Text; namespace UNitTesting { [TestFixture] public class Program {} } Test Attribute The Test attribute indicates that a method within a test fixture should be run by the Test Runner application. The method must be public, return void, and will not be run when the Test Fixture is run. The following code depicts the use of this attribute as in the following [TestFixture] public class Program { [Test] public void Test() { ... } } Assert Class the Assert class is used to confirm whether the test cases are producing the expected result or not using its auxiliary methods such as AreEqual() or AreNotEqual(). ExpectedException Attribute You could fortify your code to handle various exceptions by using try...Catch blocks. But sometimes you have some circumstances where you actually want to ensure that an exception occurs. To overcome such a problem you should use the ExpectedException attribute as in the following [TestFixture] public class Program { [Test] [ExpectedException(typeof(DivideByZeroException))] public void Test() { int i = 10, j = 0, x; x = i / j; } } In the previous code, we are intentionally committing a divide by zero exception that is to be detected during the NUnit Test execution. Ignore Attribute The Ignore attribute is required to indicate that a test should not be run on a particular method. Use the Ignore attribute as in the following: [TestFixture] public class Program { [Test] [Ignore("This method is skipping")] public void Test() { ... } } xUnit is far more flexible and extensible than the other . Net Unit test frameworks. It allows you to create new attributes to control your tests. It ensures custom functionality with the possibility of extending the Asset class's Contains, DoesNotContain Equal, NotEqual, InRange, & NotInRange. NUnit will run all the tests using the same class instance, while xUnit will create a new instance for each test. xUnit is far more flexible and extensible than the other . Net Unit test frameworks. It allows you to create new attributes to control your tests. It ensures custom functionality with the possibility of extending the Asset class's Contains, DoesNotContain Equal, NotEqual, InRange, & NotInRange Mocking Frameworks (Moq, NSubstitute, Rhino Mocks, FakeItEasy, and NMock3) are used to create fake objects. We can stub, i.e., completely replace the body of member and function. It is used to isolate each dependency and help developers in performing unit testing in a concise, quick, and reliable way. In an Xunit test class or fixture, there are two kinds of tests: Fact tests and Theory tests. The small, but very important, difference is that Theory tests are parameterized and can take outside input. Fact tests, however, are not parameterized and cannot take outside input How do I ignore tests in xUnit? xUnit.net does not require an attribute for a test class; it looks for all test methods in all public (exported) classes in the assembly. Set the Skip parameter on the [Fact] attribute to temporarily skip a test. When using the default testing framework in .NET your tests will be decorated with a testmethod attribute and you can just place the ignore attribute above them. When you have the ignore attribute above a testmethod the test will be skipped. For example: [TestMethod] [Ignore] public void TestStartAcquireEmpty() { } However I was using Xunit. And when using Xunit the syntax is a bit different. I was having trouble finding the correct way to skip a test in Xunit so here it is: in Xunit you don’t have testmethods but tests are decorated with the fact attribute. Like so: [Fact] public void PassingTest() { Assert.Equal(4, Add(2, 2)); } In order to skip a test (or fact) you need to pass in the skip parameter, followed by a reason why you decided to skip the test. For example: temporarily disabled features, technical debt, … and so on. Here is an example: [Fact(Skip = "Polly retry disabled for update address")] public async void UpdateAddress_Throws_ObjectIsNotUpdatedException(){//....} Now when you run your tests you will see that this test has been skipped. Indicated by the yellow exclamation mark. The output will also tell you why the test was skipped: And last but not least along with the amount of failed and succeeded tests, Visual Studio will also tell you how many tests were skipped. What is a stub? The stub is an interface that has a minimum count of methods to simulate the actual object. It is an object that has preexisting data and returns a fixed value irrespective of input. Moreover, we mainly use it when we want to avoid a response from the real object. Primarily, we use it wherever the test suite is uncomplicated, and having hard-coded values is not a problem. Additionally, both developers and testers use it. But we can not share it mainly for compatibility concerns occurring due to hard-coding of resources, deployment dependencies, and platforms. //Play button stub method public void playbtn(){ System.out.println("Play button working fine"); } //Exit button stub method public void exitbtn(){ System.out.println("Exit button working fine"); } //Up Arrow stub method public void upArw(){ System.out.println("Up arrow working fine"); } //Down Arrow stub method public void downArw(){ System.out.println("Down arrow working fine"); } Next, to implement the above actions as a stub, we will include the logic as depicted below: if(button = "Play"){ playbtn(); } if(button = "Exit"){ exitbtn(); } if(button = "Uparrow"){ upArw(); } if(button = "Downarrow"){ downArw(); } What is mock? The mock is an interface that we program to check the outputs that are results of the tests against the expected results. We commonly implement it by taking the help of third-party libraries like Mockito, JMock. It is also helpful when we have a big test suite, and each test requires a unique set up of data. In such a scenario, maintaining a stub becomes an expensive affair. A mock allows maintaining data configuration within the test. Moreover, both the developers and testers can use it. But we can not share it with a broader group due to compatibility concerns occurring from hard-coding of resources, deployment dependencies, and platforms. Additionally, a mock can also count how many times we call a method, function, etc. Or the sequence of calls to an object. Moreover, it checks the communications among classes. We can use the method mock() for mocking purposes. Subsequently, let us see the implementation for testing of mock objects via the Mockito framework. //interface public interface Mathematics{ public int sub(int a, int b){ } } // class public class Substraction{ //getter method public Mathematics getSub(){ return m; } //setter method public setSub(Mathematics m){ this.m = m; } Mathematics m; public int subNumbers(int a, int b){ return m.sub(a,b); } } The below example shows an implementation of a mock object for interface Mathematics within a Mockito test. import org.junit.Before; import org.junit.Test; import static org.mockito.Mockito.*; public class MathematicsTest{ Subtraction s; @Before public void setup(){ // mock object created for Interface Mathermatics Mathematics maths = mock(Mathematics.class); when(maths.sub(2,1)).thenReturn(1); s = new Substraction(); s.setSub(maths); } @Test public void testsubNumbers(){ Assert.assertEquals(1, s.subNumbers(2,1)); } } What is the difference between stub and mock? Conclusively, now that we have a basic understanding of stub and mock, we shall list their differences based on some features. Features Stub Mock Implementation Implemented by developers, testers by themselves, or produced via tools. The developers implement it via a third-party library like Mockito, JMock, and WireMock. Scenario Uncomplicated test suites use it. Big test suites use it. User Interface No graphical user interface. It has a graphical user interface. Data Setup Data gets hard-coded. Here, data configuration happens by the tests. Purpose Its purpose is state verification. Its purpose is characteristics verification. Coupling of Data It tightly couples with test data. Workable with both tightly and loosely coupled test data. Stateful No Yes Advantages Here, Free tools are available, along with plenty of resources online. Here, Open-source tools are available, along with plenty of resources online. Disadvantages Test cases couple tightly due to the hard-coding of data. Mostly used by developers and not by testers. Usage We use it if the test data requirements in the suite are simple. We use it if the test data requirements in the suite are complex also if all the data configurations happen within the tests. Technical Knowledge It requires average technical knowledge. It requires significant technical knowledge. When to use a stub? We can implement stub in the scenarios listed below: If we are developing the back end of a minor application with a third-party library to interact with an API, we can implement an interface that can disconnect us from the third-party library. Eventually, that interface will act as a stub and yield hard-coded values. Conclusively, we can use values in unit tests. If we are testing an independent application, we will implement a stub for the Rest APIs on which the application is built. Additionally, we can create it with the help of any internal tool designed by the development team. When to use a mock? We can implement a mock object in the scenarios listed below: If we are developing a back end of an application having many classes to test, we can use a Mockito framework to mock the dependent classes. If we are developing a back end of an application and we have to disconnect from the API dependencies in HTTP, we can use a mock framework like mountebank or WireMock. Then create a mock for the dependency classes in the test. Key Takeaways A stub is an object which has preexisting data and utilizes it during tests. Moreover, we mainly implement when we avoid actual objects interacting with data. Additionally, it produces an unacceptable outcome. A mock object is similar to a stub. However, it is possible to apply assertions on a mock object which is not there for a stub object.

No comments:

Post a Comment