Difference between revisions of "BigBlueButton Mobile Client Unit Tests"
(→Model Unit Tests) |
(→Flex Unit Testing and Mockolate Unit Testing Frameworks) |
||
(6 intermediate revisions by the same user not shown) | |||
Line 8: | Line 8: | ||
===Is it worth it... really??=== | ===Is it worth it... really??=== | ||
+ | |||
+ | Test driven development begins with the notion that before writing and code, you should have a well thought out design. You should prototype all of your classes and functions, and know their behaviors ahead of time. What this allows you to do, is to write tests that check and constrain the behavior of these classes and functions to ensure that they do exactly what you want them to. Assuming your design is correct, and that the unit tests verify the behavior of the classes and functions, all that remains are "implementation details". Your implementation must pass all the tests you've written, and then you can fiddle with improving the efficiency of an implementation, after correctness is ensured. | ||
+ | |||
+ | But what if you've already written the bulk of your application already, and are pretty darn sure that it's mostly correct? Unit tests offer the benefit that whenever you change something, you can run your unit tests again, and if they all pass, you can be confident that you haven't broken anything. Sometimes when you fix a bug, you accidentally introduce a new one... but with unit tests you can simply run them again to make sure that you haven't, AND write a new unit test to ensure that the bug you fixed doesn't get re-introduced in the future. | ||
+ | |||
+ | Therefore, unit testing makes your application easier to re-factor. But, unit tests do indeed make it harder to redesign your application. If you change the behavior of a class or function, or remove legacy code, this will cause unit tests to fail. If you code your application 'as you go', without any forethought on the design, then unit tests will make your job harder. In a sense though, this is not a real problem, since all that this implies is that unit testing forces you to think about good design ahead of time, instead of just hacking along. | ||
===How many unit tests should a function get?=== | ===How many unit tests should a function get?=== | ||
+ | |||
+ | There is no definite answer to this question, but some general guidelines that I like are: | ||
+ | |||
+ | *Don't test the language or the framework. If you are using a framework or library for your application, and the documentation tells you what the behavior of a function or class is, don't test it. If it fails to do what it is supposed to, this is a bug in the framework, library, or language implementation, but not your application. | ||
+ | *Don't test simple getters and setters. By "simple setter", I mean a method that accept a single argument, and simply sets the value of a field to the value of that argument. By "simple getter", I mean a method that accepts no arguments, and simply returns a value of a field. | ||
+ | *Each control structure (branching, looping, jumping/breaking, returning from a function) adds one more unit test to the function. | ||
+ | *Each special case deserves a unit test (maybe you want your function to return null in some cases, or throw an exception in response to an argument having a specific value). Generally, each way your function can possibly be ended, adds one more unit test to that function. | ||
+ | *Each event that gets dispatched adds one more unit test. | ||
==Flex Unit Testing and Mockolate Unit Testing Frameworks== | ==Flex Unit Testing and Mockolate Unit Testing Frameworks== | ||
+ | |||
+ | For general information on stubbing and mocking, see the following resources: | ||
+ | |||
+ | For information regarding the Mockolate framework specifically, see: | ||
===Resources=== | ===Resources=== | ||
==Mobile Client Unit Testing Idioms== | ==Mobile Client Unit Testing Idioms== | ||
+ | |||
+ | ===The Model=== | ||
+ | |||
+ | The way I tested the model made no use of the Mockolate framework. This is because the code for each module's model does not contains very many dependencies on other classes, and the need to create mocks and stubs wasn't there. Most unit tests simply invoked a functions, and used a " assertThat(<VALUE1>, equalTo(<VALUE2>));" statement to verify that the function produced the desired effect. If you've ever done any kind of simple testing using C's assert macro, this does basically the same thing, namely verifies that <VALUE1> is identical with <VALUE2>. | ||
+ | |||
+ | The only tricky part that took a while to figure out, was verifying that a signal gets dispatched under certain conditions, and that the values that these signals were dispatched with, were what they were supposed to be. Mockolate allows "spying" on classes to verify their behavior, but unfortunately this only works if the signals are mocked by the framework, which requires them to be public fields. In the mobile application, they are not public, and have no setters, so this was not (as far as I could figure out) possible. | ||
+ | |||
+ | The flow for unit testing these things was as follows. First, code a listener method that is to verify the values that the signal is dispatched. The verification can be done simply with "assertThat(...)". Add this 'verification' method as a listener to the signal that you want to check. Second, you are going to dispatch a custom event in this verification method, that will be used in the next step. Next, in the test case, use "Async.proceedOnEvent(...)", to listen for the custom event that you dispatch from your verification method. The Async.proceedOnEvent(...) will cause the unit test to fail if the event you are listening for does not get dispatched. This solution allows you to test that the signal was both dispatched, and with the correct values. If the signal does not get dispatched, then the verification method will never get called, and will never dispatch the event that the "Async" is waiting for, causing the unit test to fail. If the signal is dispatched, but with the wrong values, it will cause the "assertThat" checks to fail. Any further checks on the class should also be done here, because signal are dispatched asynchronously. | ||
+ | |||
+ | For an example of all this stuff, see the ****: GITUHB LINK | ||
+ | |||
+ | ===The Commands=== | ||
+ | |||
+ | ===The View Mediators=== | ||
==Unit Tests Coverage, Notes== | ==Unit Tests Coverage, Notes== | ||
− | ===Model Unit Tests=== | + | The mobile client was initially structured using the Model-View-Controller (MVC) design pattern. As time went on the code base got scattered in a way that is not entirely in keeping with the design pattern. The mobile client's code is divided into four main parts: models, views, core, and commands. The meanings of "model" and "view" are the same as in the MVC design pattern. |
+ | |||
+ | The "command" part of the application consists of signal-command pairs. .... | ||
+ | |||
+ | The "core" component is where most of the code that communicate with a BigBlueButton server ended up. I don't think this part of the application should be unit tested for two main reasons. First, most of the code in the "core" is simple, and repeats the same pattern over and over again. Second, unit testing this part of the application may make it more "brittle". This is because any time the message format for communication between client and server changes (not an infrequent occurrence), the unit tests will also need to be redesigned or they will fail. | ||
+ | |||
+ | |||
+ | |||
+ | ===Model Unit Tests (COMPLETE)=== | ||
'' <span style="color: red;">***Github links coming soon... ***</span>'' | '' <span style="color: red;">***Github links coming soon... ***</span>'' | ||
Line 27: | Line 67: | ||
!Module!!Class Name!!Coverage!!Notes!!Github | !Module!!Class Name!!Coverage!!Notes!!Github | ||
|- | |- | ||
− | |Chat||ChatMessage||Complete||Simple class. The tests just make sure that XML tags are stripped from chat messages.|| | + | |Chat||ChatMessage||Complete||Simple class. The tests just make sure that XML tags are stripped from chat messages.|| github link |
+ | |- | ||
+ | |Chat||ChatMessages||Complete|| - || github link | ||
+ | |- | ||
+ | |Chat||ChatMessagesSession||Complete|| - || github link | ||
+ | |- | ||
+ | |Chat||ChatMessageVO||None||Too simple. Not worth testing. || github link | ||
+ | |- | ||
+ | |Chat||PrivateChatMessage||None||Too simple, not worth testing. || github link | ||
+ | |- | ||
+ | |Presentation||Presentation||Complete || - || github link | ||
|- | |- | ||
− | | | + | |Presentation||PresentationList||Complete || Very simple, almost not worth testing? || github link |
+ | |- | ||
+ | |Presentation||Slide||Complete || - || github link | ||
+ | |- | ||
+ | |User||User||None||Too simple. Not worth testing. || github link | ||
+ | |- | ||
+ | |User||UserList||Complete||Big class. Large number of unit tests... || github link | ||
+ | |- | ||
+ | |User||UserSession||None||Too Simple. Not worth testing. || github link | ||
+ | |- | ||
+ | |User||UserUISession||Complete|| - || github link | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | ===Command Unit Tests=== | ||
+ | |||
+ | |||
+ | '' <span style="color: red;">***Github links coming soon... ***</span>'' | ||
+ | |||
+ | {|cellspacing="0" width="100%" cellpadding="5" border="1" | ||
+ | |- | ||
+ | !Command Name!!Unit Tests? (Yes/No)!!Coverage!!Notes!!Github | ||
+ | |- | ||
+ | |CameraQualityCommand||Yes||Complete||-|| github link | ||
+ | |- | ||
+ | |ConnectCommand||Yes||Complete|| - || github link | ||
+ | |- | ||
+ | |DisconnectUserCommand||-||-|| - || github link | ||
+ | |- | ||
+ | |JoinMeetingCommand||-||-||-|| github link | ||
+ | |- | ||
+ | |LoadSlideCommand||-||-||-|| github link | ||
+ | |- | ||
+ | |MicrophoneMuteCommand||Yes||Complete|| - || github link | ||
+ | |- | ||
+ | |NavigateToCommand||-||-||-|| github link | ||
+ | |- | ||
+ | |RaiseHandCommand||-||-|| - || github link | ||
+ | |- | ||
+ | |RemovePageCommand||-||-||-|| github link | ||
+ | |- | ||
+ | |ShareCameraCommand||Yes||Complete||-|| github link | ||
+ | |- | ||
+ | |ShareMicrophoneCommand||Yes||Complete||-|| github link | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | ===View Mediator (for pages' views) Unit Tests=== | ||
+ | |||
+ | |||
+ | '' <span style="color: red;">***Github links coming soon... ***</span>'' | ||
+ | |||
+ | {|cellspacing="0" width="100%" cellpadding="5" border="1" | ||
|- | |- | ||
− | + | !View Mediator Name!!Unit Tests? (Yes/No)!!Coverage!!Notes!!Github | |
|- | |- | ||
− | | | + | |ChatViewMediator||Yes||Complete||-|| github link |
|- | |- | ||
− | | | + | |ChatRoomsViewMediator||Yes||Complete|| - || github link |
|- | |- | ||
− | | | + | |DeskshareViewMediator||Yes||-|| - || github link |
|- | |- | ||
− | | | + | |ParticipantsViewMediator||Yes||-||-|| github link |
|- | |- | ||
− | | | + | |PresentationViewMediator||Yes||-||-|| github link |
|- | |- | ||
− | | | + | |ProfileViewMediator||Yes||Complete|| - || github link |
|- | |- | ||
− | | | + | |SelectParticipantViewMediator||Yes||-||-|| github link |
|- | |- | ||
− | | | + | |UserDetailsViewMediator||Yes||-|| - || github link |
|- | |- | ||
− | | | + | |VideoChatViewMediator||Yes||-||-|| github link |
|- | |- | ||
|} | |} |
Latest revision as of 14:57, 14 January 2015
***Under construction!!!***
Contents
Unit Testing Philosophy
What is unit testing? Why bother?
Anyone who writes any sort of non-trivial code knows that mistakes are very easy to make. Overlooking special edge cases, or failing to do null checks when they are not enforced by the way your code is typed, for example, can cause your application to crash. Being certain that the code you write is absolutely correct is a difficult problem to solve. Unit testing aims to partially solve this problem, building on the insight that if all of the parts of a program do the correct thing, then the program as a whole will do the correct thing. Therefore, we should aim to test the parts of an application as exhaustively as possible. The parts we test (typically functions or classes) are called 'units'.
Is it worth it... really??
Test driven development begins with the notion that before writing and code, you should have a well thought out design. You should prototype all of your classes and functions, and know their behaviors ahead of time. What this allows you to do, is to write tests that check and constrain the behavior of these classes and functions to ensure that they do exactly what you want them to. Assuming your design is correct, and that the unit tests verify the behavior of the classes and functions, all that remains are "implementation details". Your implementation must pass all the tests you've written, and then you can fiddle with improving the efficiency of an implementation, after correctness is ensured.
But what if you've already written the bulk of your application already, and are pretty darn sure that it's mostly correct? Unit tests offer the benefit that whenever you change something, you can run your unit tests again, and if they all pass, you can be confident that you haven't broken anything. Sometimes when you fix a bug, you accidentally introduce a new one... but with unit tests you can simply run them again to make sure that you haven't, AND write a new unit test to ensure that the bug you fixed doesn't get re-introduced in the future.
Therefore, unit testing makes your application easier to re-factor. But, unit tests do indeed make it harder to redesign your application. If you change the behavior of a class or function, or remove legacy code, this will cause unit tests to fail. If you code your application 'as you go', without any forethought on the design, then unit tests will make your job harder. In a sense though, this is not a real problem, since all that this implies is that unit testing forces you to think about good design ahead of time, instead of just hacking along.
How many unit tests should a function get?
There is no definite answer to this question, but some general guidelines that I like are:
- Don't test the language or the framework. If you are using a framework or library for your application, and the documentation tells you what the behavior of a function or class is, don't test it. If it fails to do what it is supposed to, this is a bug in the framework, library, or language implementation, but not your application.
- Don't test simple getters and setters. By "simple setter", I mean a method that accept a single argument, and simply sets the value of a field to the value of that argument. By "simple getter", I mean a method that accepts no arguments, and simply returns a value of a field.
- Each control structure (branching, looping, jumping/breaking, returning from a function) adds one more unit test to the function.
- Each special case deserves a unit test (maybe you want your function to return null in some cases, or throw an exception in response to an argument having a specific value). Generally, each way your function can possibly be ended, adds one more unit test to that function.
- Each event that gets dispatched adds one more unit test.
Flex Unit Testing and Mockolate Unit Testing Frameworks
For general information on stubbing and mocking, see the following resources:
For information regarding the Mockolate framework specifically, see:
Resources
Mobile Client Unit Testing Idioms
The Model
The way I tested the model made no use of the Mockolate framework. This is because the code for each module's model does not contains very many dependencies on other classes, and the need to create mocks and stubs wasn't there. Most unit tests simply invoked a functions, and used a " assertThat(<VALUE1>, equalTo(<VALUE2>));" statement to verify that the function produced the desired effect. If you've ever done any kind of simple testing using C's assert macro, this does basically the same thing, namely verifies that <VALUE1> is identical with <VALUE2>.
The only tricky part that took a while to figure out, was verifying that a signal gets dispatched under certain conditions, and that the values that these signals were dispatched with, were what they were supposed to be. Mockolate allows "spying" on classes to verify their behavior, but unfortunately this only works if the signals are mocked by the framework, which requires them to be public fields. In the mobile application, they are not public, and have no setters, so this was not (as far as I could figure out) possible.
The flow for unit testing these things was as follows. First, code a listener method that is to verify the values that the signal is dispatched. The verification can be done simply with "assertThat(...)". Add this 'verification' method as a listener to the signal that you want to check. Second, you are going to dispatch a custom event in this verification method, that will be used in the next step. Next, in the test case, use "Async.proceedOnEvent(...)", to listen for the custom event that you dispatch from your verification method. The Async.proceedOnEvent(...) will cause the unit test to fail if the event you are listening for does not get dispatched. This solution allows you to test that the signal was both dispatched, and with the correct values. If the signal does not get dispatched, then the verification method will never get called, and will never dispatch the event that the "Async" is waiting for, causing the unit test to fail. If the signal is dispatched, but with the wrong values, it will cause the "assertThat" checks to fail. Any further checks on the class should also be done here, because signal are dispatched asynchronously.
For an example of all this stuff, see the ****: GITUHB LINK
The Commands
The View Mediators
Unit Tests Coverage, Notes
The mobile client was initially structured using the Model-View-Controller (MVC) design pattern. As time went on the code base got scattered in a way that is not entirely in keeping with the design pattern. The mobile client's code is divided into four main parts: models, views, core, and commands. The meanings of "model" and "view" are the same as in the MVC design pattern.
The "command" part of the application consists of signal-command pairs. ....
The "core" component is where most of the code that communicate with a BigBlueButton server ended up. I don't think this part of the application should be unit tested for two main reasons. First, most of the code in the "core" is simple, and repeats the same pattern over and over again. Second, unit testing this part of the application may make it more "brittle". This is because any time the message format for communication between client and server changes (not an infrequent occurrence), the unit tests will also need to be redesigned or they will fail.
Model Unit Tests (COMPLETE)
***Github links coming soon... ***
Module | Class Name | Coverage | Notes | Github |
---|---|---|---|---|
Chat | ChatMessage | Complete | Simple class. The tests just make sure that XML tags are stripped from chat messages. | github link |
Chat | ChatMessages | Complete | - | github link |
Chat | ChatMessagesSession | Complete | - | github link |
Chat | ChatMessageVO | None | Too simple. Not worth testing. | github link |
Chat | PrivateChatMessage | None | Too simple, not worth testing. | github link |
Presentation | Presentation | Complete | - | github link |
Presentation | PresentationList | Complete | Very simple, almost not worth testing? | github link |
Presentation | Slide | Complete | - | github link |
User | User | None | Too simple. Not worth testing. | github link |
User | UserList | Complete | Big class. Large number of unit tests... | github link |
User | UserSession | None | Too Simple. Not worth testing. | github link |
User | UserUISession | Complete | - | github link |
Command Unit Tests
***Github links coming soon... ***
Command Name | Unit Tests? (Yes/No) | Coverage | Notes | Github |
---|---|---|---|---|
CameraQualityCommand | Yes | Complete | - | github link |
ConnectCommand | Yes | Complete | - | github link |
DisconnectUserCommand | - | - | - | github link |
JoinMeetingCommand | - | - | - | github link |
LoadSlideCommand | - | - | - | github link |
MicrophoneMuteCommand | Yes | Complete | - | github link |
NavigateToCommand | - | - | - | github link |
RaiseHandCommand | - | - | - | github link |
RemovePageCommand | - | - | - | github link |
ShareCameraCommand | Yes | Complete | - | github link |
ShareMicrophoneCommand | Yes | Complete | - | github link |
View Mediator (for pages' views) Unit Tests
***Github links coming soon... ***
View Mediator Name | Unit Tests? (Yes/No) | Coverage | Notes | Github |
---|---|---|---|---|
ChatViewMediator | Yes | Complete | - | github link |
ChatRoomsViewMediator | Yes | Complete | - | github link |
DeskshareViewMediator | Yes | - | - | github link |
ParticipantsViewMediator | Yes | - | - | github link |
PresentationViewMediator | Yes | - | - | github link |
ProfileViewMediator | Yes | Complete | - | github link |
SelectParticipantViewMediator | Yes | - | - | github link |
UserDetailsViewMediator | Yes | - | - | github link |
VideoChatViewMediator | Yes | - | - | github link |