白盒测试(white-box testing)又称透明盒测试(glass box testing)、结构测试(structural testing)等,软件测试的主要方法之一,也称结构测试、逻辑驱动测试或基于程序本身的测试。测试应用程序的内部结构或运作,而不是测试应用程序的功能(即黑盒测试)。在白盒测试时,以编程语言的角度来设计测试案例。测试者输入数据验证数据流在程序中的流动路径,并确定适当的输出,类似测试电路中的节点。测试者了解待测试程序的内部结构、算法等信息,这是从程序设计者的角度对程序进行的测试。
白盒测试可以应用于单元测试(unit testing)、集成测试(integration testing)和系统的软件测试流程,可测试在集成过程中每一单元之间的路径,或者主系统跟子系统中的测试。尽管这种测试的方法可以发现许多的错误或问题,它可能无法检测未使用部分的规范。
如果我们能获得win8应用代码的话,那么就可以使用Visual Studio进行有关白盒测试,这里我们不妨选择其自带的模板单元测试进行设计,下面是实验的步骤:
(一)创建解决方案和单元测试项目:
(1) 在“文件”菜单上选择“新建”,然后选择“新建项目”。
(2) 在“新建项目”对话框中,展开“已安装”、“Visual C#”,选择“Windows Store”。 然后从项目模板列表中选择“空白应用程序”。
(3) 将项目命名为 isLeapYear,并确保选中“创建解决方案的目录”。
(4) 在解决方案资源管理器中,选择解决方案名称,从快捷菜单中选择“添加”,然后选择“新建项目”。
(5) 在“新建项目”对话框中,展开“已安装”、“Visual C#”,然后选择“Windows 应用商店”。 然后从项目模板列表中选择“单元测试库(Windows Store 应用程序)”。
(6) 在 Visual Studio 编辑器中打开 UnitTest1.cs。
请注意:
- 每个测试都是使用 [TestMethod] 定义的。 测试方法必须返回 void,并且不能具有任何参数。
- 测试方法必须位于使用 [TestClass] 特性修饰的类中。
运行测试时,将为每个测试类创建一个实例。 将按未指定顺序调用测试方法。
向isLeapYear项目添加Judge类:
(1) 在“解决方案资源管理器”中,选择“isLeapYear”项目名称。 从快捷 菜单中选择“添加”,然后选择“类”。
(2) 将类文件命名为 Judge.cs
(3) 将以下代码添加到 Judge 类 Judge.cs 文件中:
将测试项目合并为应用程序项目:
(1) 将对 isLeapYear 应用程序的引用添加到 UnitTestLibrary1项目。
- 在解决方案资源管理器中,选择“UnitTestLibrary1”项目,然后选择快捷菜单上的“添加引用...”。
- 在“添加引用 - UnitTestLibrary1”对话框上,展开“解决方案”,再选择“项目”。 然后选择“isLeapYear”项目。
(2) 向 UnitTest1.cs 文件添加 using 语句:
- 打开 UnitTest1.cs。
- 在using语句中添加using isLeapYear;
(3) 添加使用 Judge 函数的测试。 将下列代码添加到 UnitTest1.cs:
(4) 生成解决方案。新测试将显示在测试资源管理器的“未运行的测试”节点中。
(5) 在测试资源管理器中,选择“全部运行”。
(二)根据代码画出程序流程图和程序流图:
图1-程序流程图
图2-程序流图
(三)实验分析
- 测试用例和结果
测试环境:Win8系统+Visual Studio 2013 Ultimate
(flag = 1 表示该年是闰年;flag = 0表示该年不是闰年;flag = -1表示非法输入; flag=2表示在公元1582年后才有“闰年”一说)
表1--测试用例
测试编号 | 输入 | 预期输出 | 实际输出 | 覆盖路径 | 是否通过测试 |
1 | 1400 | flag=2 | flag=2 | 1→2→3→4→5 | 是 |
2 |
| flag=-1 | flag=-1 | 1→2→11→12 | 是 |
3 | 2000 | flag=1 | flag=1 | 1→2→3→6→7→8 | 是 |
4 | 1864 | flag=1 | flag=1 | 1→2→3→6→9→10→7→8 | 是 |
5 | 2002 | flag=0 | flag=0 | 1→2→3→6→9→10→13→14 | 是 |
6 | 1700 | flag=0 | flag=0 | 1→2→3→6→9→13→14 | 是 |
7 | 1769 | flag=0 | flag=0 | 1→2→3→6→9→10→13→14 | 是 |
8 | -298 | flag=2 | flag=2 | 1→2→3→4→5 | 是 |
9 | Abc12 | flag=1 | flag=1 | 1→2→11→12 | 是 |
10 | 中国 | flag=1 | flag=1 | 1→2→11→12 | 是 |
针对各类测试用例的过程截图(每类路径举例一个)
(1)
(2)
(3)
(4)
(5)
(6)
(四)实验结果
经过验证,通过手动添加断言,来判断测试是否通过,即Assert.AreEqual(expected, test); 如果expected和test不等,则表示测试通过反之不通过,在该应用中通过验证所有测试用例均通过,因而暂没发现bug。
结果截图:(分别为测试通过和未通过的截图,其他类似,不再一一罗列)
(五)实验结论及源代码
在本实验中使用的是路径覆盖,通过程序流程图和程序流图得出测试用例,因而具有全面性和代表性。当然白盒测试并非完美,亦有不足之处,比如无法检测代码中遗漏的路径和数据敏感性错误、代价昂贵等。
(1)App.xaml.cs:
1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Linq; 5 using System.Runtime.InteropServices.WindowsRuntime; 6 using Windows.ApplicationModel; 7 using Windows.ApplicationModel.Activation; 8 using Windows.Foundation; 9 using Windows.Foundation.Collections; 10 using Windows.UI.Xaml; 11 using Windows.UI.Xaml.Controls; 12 using Windows.UI.Xaml.Controls.Primitives; 13 using Windows.UI.Xaml.Data; 14 using Windows.UI.Xaml.Input; 15 using Windows.UI.Xaml.Media; 16 using Windows.UI.Xaml.Navigation; 17 18 // “空白应用程序”模板在 http://go.microsoft.com/fwlink/?LinkId=234227 上有介绍 19 20 namespace isLeapYear 21 { 22 ///23 /// 提供特定于应用程序的行为,以补充默认的应用程序类。 24 /// 25 sealed partial class App : Application 26 { 27 ///28 /// 初始化单一实例应用程序对象。 这是执行的创作代码的第一行, 29 /// 逻辑上等同于 main() 或 WinMain()。 30 /// 31 public App() 32 { 33 this.InitializeComponent(); 34 this.Suspending += OnSuspending; 35 } 36 37 ///38 /// 在应用程序由最终用户正常启动时进行调用。 39 /// 以打开特定文件等情况下使用其他入口点。 40 /// 41 /// 有关启动请求和过程的详细信息。 42 protected override void OnLaunched(LaunchActivatedEventArgs e) 43 { 44 45 #if DEBUG 46 if (System.Diagnostics.Debugger.IsAttached) 47 { 48 this.DebugSettings.EnableFrameRateCounter = true; 49 } 50 #endif 51 52 Frame rootFrame = Window.Current.Content as Frame; 53 54 // 不要在窗口已包含内容时重复应用程序初始化, 55 // 只需确保窗口处于活动状态 56 if (rootFrame == null) 57 { 58 // 创建要充当导航上下文的框架,并导航到第一页 59 rootFrame = new Frame(); 60 //设置默认语言 61 rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0]; 62 63 rootFrame.NavigationFailed += OnNavigationFailed; 64 65 if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) 66 { 67 //TODO: 从之前挂起的应用程序加载状态 68 } 69 70 // 将框架放在当前窗口中 71 Window.Current.Content = rootFrame; 72 } 73 74 if (rootFrame.Content == null) 75 { 76 // 当未还原导航堆栈时,导航到第一页, 77 // 并通过将所需信息作为导航参数传入来配置 78 // 参数 79 rootFrame.Navigate(typeof(MainPage), e.Arguments); 80 } 81 // 确保当前窗口处于活动状态 82 Window.Current.Activate(); 83 } 84 85 ///86 ///导航到特定页失败时调用 87 /// 88 ///导航失败的框架 89 ///有关导航失败的详细信息 90 void OnNavigationFailed(object sender, NavigationFailedEventArgs e) 91 { 92 throw new Exception("Failed to load Page " + e.SourcePageType.FullName); 93 } 94 95 ///96 /// 在将要挂起应用程序执行时调用。 在不知道应用程序 97 /// 将被终止还是恢复的情况下保存应用程序状态, 98 /// 并让内存内容保持不变。 99 /// 100 /// 挂起的请求的源。101 /// 有关挂起的请求的详细信息。102 private void OnSuspending(object sender, SuspendingEventArgs e)103 {104 var deferral = e.SuspendingOperation.GetDeferral();105 //TODO: 保存应用程序状态并停止任何后台活动106 deferral.Complete();107 }108 }109 }
(2)MainPage.xaml.cs:
using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Runtime.InteropServices.WindowsRuntime;using Windows.Foundation;using Windows.Foundation.Collections;using Windows.UI.Xaml;using Windows.UI.Xaml.Controls;using Windows.UI.Xaml.Controls.Primitives;using Windows.UI.Xaml.Data;using Windows.UI.Xaml.Input;using Windows.UI.Xaml.Media;using Windows.UI.Xaml.Navigation;// “空白页”项模板在 http://go.microsoft.com/fwlink/?LinkId=234238 上有介绍namespace isLeapYear{ ////// 可用于自身或导航至 Frame 内部的空白页。 /// public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } private void button_Click(object sender, RoutedEventArgs e) { Frame.Navigate(typeof(Result), textBox.Text); } }}
(3)Result.xaml.cs:
1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Linq; 5 using System.Runtime.InteropServices.WindowsRuntime; 6 using Windows.Foundation; 7 using Windows.Foundation.Collections; 8 using Windows.UI.Xaml; 9 using Windows.UI.Xaml.Controls;10 using Windows.UI.Xaml.Controls.Primitives;11 using Windows.UI.Xaml.Data;12 using Windows.UI.Xaml.Input;13 using Windows.UI.Xaml.Media;14 using Windows.UI.Xaml.Navigation;15 16 17 // “空白页”项模板在 http://go.microsoft.com/fwlink/?LinkId=234238 上有介绍18 19 namespace isLeapYear20 {21 ///22 /// 可用于自身或导航至 Frame 内部的空白页。23 /// 24 public sealed partial class Result : Page25 {26 public Result()27 {28 this.InitializeComponent();29 }30 protected override void OnNavigatedTo(NavigationEventArgs e)31 {32 string year = e.Parameter as String; 33 //判断传递数据是否为空34 Judge judge = new Judge();35 if (judge.isLeapYear(year) == 1)36 textBlock.Text = year + "年是闰年";37 else if (judge.isLeapYear(year) == 0)38 textBlock.Text = year + "年不是闰年";39 else if (judge.isLeapYear(year) == 2)40 textBlock.Text = "公元1582年后方有\"闰年\"一说";41 else42 textBlock.Text = "请输入合法年份";43 44 }45 46 private void button1_Click(object sender, RoutedEventArgs e)47 {48 Frame.Navigate(typeof(MainPage));49 } 50 }51 }
(4)Judge.cs:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace isLeapYear 8 { 9 public class Judge10 {11 public Judge()12 {13 14 }15 public int isLeapYear(String year)16 {17 int flag;18 try19 {20 int test = int.Parse(year);21 if (test <= 1582)22 flag = 2;23 else if (test % 400 == 0)24 flag = 1;25 else if (test % 100 == 0)26 flag = 0;27 else if (test % 4 == 0)28 flag = 1;29 else30 flag = 0;31 }32 catch (Exception ex)33 {34 flag = -1;35 }36 return flag;37 }38 }39 }40 (5) UnitTest.cs:41 using System;42 using System.Collections.Generic;43 using System.Linq;44 using System.Text;45 using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;46 using isLeapYear;47 48 namespace UnitTestLibrary149 {50 [TestClass]51 public class UnitTest152 {53 [TestMethod]54 public void TestMethod1()55 {56 Judge judge = new Judge();57 String year = "2012";58 int test = judge.isLeapYear(year);59 int expected = 1;60 Assert.AreEqual(expected, test);61 }62 63 }64 }