Which one do we prefer!
Either Single test with multiple group support. Data provider providing data
#DataProvider(name = "theDataProvider")
fun theDataProvider(): Array<String> = arrayOf(
if (realmValue.equals(US, true)) input["inputForSuccessUS"].toString() // It could be realm or check the current group being executed from context. Also switch instead of if else
else input["inputForSuccessEU"].toString()
)
#Test(groups = [EU, US], dataProvider = "theDataProvider")
fun `theTest`(input: String) {
executionAndValidateSuccessful(input)
}
OR Have multiple test cases for multiple regions!
#Test(groups = [EU])
fun `theTest`() {
executionAndValidateSuccessful(input["inputForSuccessEU"].toString())
}
#Test(groups = [US])
fun `theTest`() {
executionAndValidateSuccessful(input["inputForSuccessUS"].toString())
}
My team prefers option 2 while I do prefer option 1 -
Team Reasons - Do not prefer if else block as it makes the code more complicated
My Reasons - test groups and data provider are two separate tools we can use. With test group we will not write the same tests again and again and it's more easier to maintain. Also, with a separate data provider block, the tests will be independent of who is providing the data. I feel the tests will be more readable, maintainable and most importantly concise.
What do you say?
Related
I have written a code part of which is as below
Object Cal{
def mergedatasets(df: Dataset[Row], df1: Dataset[Row],df2: Dataset[Row]):Dataset[Row]={
df.union(df1).union(df2)
//other logic
}
}
object readDataframes{
def readFirstDF(spark:SparkSession):Dataset[Row]={
spark.read.json(somefile)
}
def readSecondDF(spark:SparkSession):Dataset[Row]={
spark.read.json(somefile)
}
def readThirdDF(spark:SparkSession):Dataset[Row]={
spark.read.json(somefile)
}
}
In the above code I am reading 3 files and then merging them into one which I use further for processing.
Based on the above scenario my questions are as follows:
Does it make sense to Unit test the function mergdatasets? If yes, What are the basic/minimal things to test for?How to check for corner cases, if any?
Does it make sense to Unit test readDataframes?If yes what to test for ?Would it be to check if inferred schema is as expected? and anything else?
I would like to extend the above questions for the following functions too
def timeIntervalAgg(df: Dataset[Row]): Dataset[Row] = {
val timeInterval = df
.groupBy("id","page_number")
.agg(sum("timeInterval").alias("timeInterval"))
timeIntervalAgg
}
def timeInterval(df: Dataset[Row]): Dataset[Row] ={
val windowSpec = Window.partitionBy("id").orderBy("date_time")
val timeFmt = "yyyy-MM-dd'T'HH:mm:ss"
val endTime = lead(col("date_time"),1).over(windowSpec)
val startTime = col("date_time")
val timeDiff = (unix_timestamp(endTime, timeFmt)
- unix_timestamp(startTime, timeFmt))
val timeInterval = df
.withColumn("timeInterval", lit(when(col("event") === "this_event",lit(null)
.cast("long"))
.otherwise(timeDiff)))
.where("""event != "this_event" """)
timeInterval
}
def addOddpages(df: Dataset[Row]) :Dataset[Row] = {
val odd = df
.where("""view_mode = "twin" """)
.withColumn("page_odd", col("page") + 1)
.drop("page")
.select(col("id"), col("date_time")
.cast("timestamp"),col("page_odd")
.alias("page"), col("page_view_mode"),
col("event"),col("timeInterval"))
val timeIntervalWithoddPage = df.union(odd)
timeIntervalWithoddPage
}
Please suggest if it is needed to refactor the code in a better way
to enable better testing.
My goal is to understand what to test for? what to look out while
writing test for code like above? All this questions are for Spark
code Unit testing not other language code testing.
How to unit test without redundantly testing spark which is already
tested?
Is it needed to test every function like this(since the logic/code is not very complicated) or is best to test the
function that combines the above functions in proper order.By doing
so can it be called unit testing?
Please feel free to share some sample Unit tests that you may write
for above code.
Read JSON files: If you just read JSON files, you don't need to test this.
In addition, it might be better to read the files with explicit schema in schema() to avoid some issues with inferred schema. Also, you do not need 3 identical methods for reading the files.
Union Datasets: Since Spark 2.3.0 there is unionByName() function.
That function resolves columns by name (not by position). You can consider the functions to avoid issues with union when your DataFrames have different order of columns. Of course, this function doesn't need to be tested. It's hard to say about the //other logic code inside the mergedatasets() method.
For unit testing, you can use ScalaTest or other tools.
Create SparkSession with master("local");
Create a DataFrame with the expected data;
Create an input DataFrame for each method you want to test.;
Compare expected and actual DataFrames;
The following project might be useful. You can find there how to compare two DataFrames. Also, there are several examples in the README: https://github.com/MrPowers/spark-fast-tests
I have one doubt about How to name test method, becuase I Lastly have doing unit test to a big project, and based in my experience I think that I am set name test method bad or worse, Here an example about my code.
public function notificationApproved(Request $request, User $user) {
$user = $user->getId();
$request = $request->getRequest();
$this->notification = $this->em->getRepository('AppBundle:Notification')->find(Notification::APPROVED);
$this->notificationCategory = NotificationCategory::APPROVED;
$this->notificationStatus = $this->em->getRepository('AppBundle:NotificationStatus')->find(1);
$this->reason = $reason;
//notification approvers project
foreach ($projectHasUserUnits as $keyData => $valueData) {
$projectUserUnitResponsability = $valueData->getProjectUserUnitResponsability()->last();
$responsability = $projectUserUnitResponsability->getResponsabilityProject();
if (is_null($projectUserUnitResponsability->getEndAt()) && ($responsability->getId() == Responsability::TYPE_RESPONSIBLE || $responsability->getId() == Responsability::TYPE_ACCOUNT_MANAGER || $responsability->getId() == Responsability::TYPE_PROJECT_MANAGER)) {
$user = $valueData->getUserHasUnit()->getUser();
if( !in_array($user,$this->users)){
$this->users[] = $user;
$description_label = 'notification_description_project_50';
$short_description = 'notification_content_project_50';
$notification = $this->generateNotification($Project, $user, $description_label,$short_description);
if ($notification) {
$this->notificationTrigger
->sendProjectNotification($user, $notification, $Project
, $notification->getDescription(), date('l m-d-y H:i a'));
}
}
}
}
}
it is a big method, I know, but don't worry for its logic, Just see the conditionals, and think how to name a method test like this, In this case, when itself come in all conditionals... maybe:
test_notificationApprovedWhenAllConditionsAreTrue
test_notificationApprovedWhenProjectHasUserUnitsIsBiggerThanZeroAndProjectUserUnitResponsabilityGetEndAtIsNotNullAndResponsabilityGetIdIsEqualToResponsabilityTYPERESPONSABILITYorResponsabilityGetIdIsEqualToResponsabilityTYPE_PROJECT_MANAGERanduserInArrayIsTrueAndNotificationIsTrue...
You only imagine read that !
However when it come only one conditional is easier that the above, !Of Course!, like this:
test_notificationApprovedWhenProjectHasUserUnitsIsBiggerThanZeroAndProjectUserUnitResponsabilityGetEndAtIsNotNullAndResponsabilityGetIdIsEqualToResponsabilityTYPERESPONSABILITYorResponsabilityGetIdIsEqualToResponsabilityTYPE_PROJECT_MANAGER
I've tried with comments inside test, but If a test fail, the idea would be guiding for its name (to fix the error rapidly)
So, do you have any idea ?
I have good experience with given<one-or-more-conditions>_action_result naming of test methods. Just a simple example for your case:
test_givenUserUnitGreaterThanZeroAndProjectIdEqualToOne_whenApproveNotification_thenNotificationIsSent()
I've simplified things for the sake of readability but you get the point. This nomenclature is taken from behavior-driven test frameworks. In languages that do not need the test_ you can save it.
Tests are really good indicators for code quality. Generally speaking if writing unit tests is easy then it's because the code to test is of "good" quality. You can follow some simple guidelines:
List every precondition exactly as it is necessary for the use case
Exactly one action. If not make multiple tests.
Exactly one result. If not extract methods and test them separately.
If method names get too long because of many preconditions then it might be time to extract a new class because this one has too many responsibilities
A bit of advice: if you'd add a test annotation, you would not need to prefix method names with test.
/**
* #test
*/
function givenThis_producesThat () {
As the question states, is there any downside in referencing the service directly in the template as such :
[disabled]="stateService.selectedClient == null || stateService.currentStep == 1"
In my opinion this doesn't seem like good practice and I'd much rather keep a "selectedClient" object in whatever component needs to use it. How can I get the state and store it into local variables, while observing the changes:
example: I want to move from step1 to step2 by changing "currentStep" in the "stateService", however I want the component that keeps "currentStep" ALSO as a local variable to reflect the change in the state?
Is it good practice to reference services in html templates in Angular
2?
I'd generally avoid it. It seems to bring more chaos than good.
Cons:
Coming from OOP background, this approach looks like it breaks the Law of Demeter, but more importantly,
It's no longer MVC, where your controller (Angular2's Component) acts like a mediator between the view and the services.
Like Ced said, what if a call to a service's member is costly and we need to refer to it multiple times in the view?
At the moment my editor of choice (VS Code) does not fully support Angular2 templates; referencing too many things outside of its own Component's scope in a template makes refactoring not fun anymore.
Pros:
Sometimes it looks more elegant (because it saves you 2 lines of code), but trust me, it's not.
How can I get the state and store it into local variables, while
observing the changes
Madhu Ranjan has a good answer to this. I'll just try to make it more complete here for your particular example:
In your StateService, define:
currentStep : Subject<number> = new Subject<number>();
selectedClient: Subject<Client> = new Subject<Client>();
changeStep(nextStep: number){
this.currentStep.next(nextStep);
}
selectClient(client: Client) {
this.selectedClient.next(client);
}
In your Component:
currentStep: number;
constructor(stateService : StateService){
stateService.currentStep.combineLatest(
stateService.selectedClient,
(currStep, client) => {
if (client == null) {
// I'm assuming you are not showing any step here, replace it with your logic
return -1;
}
return currStep;
})
.subscribe(val => {
this.currentStep = val;
});
}
You may try below,
stateService
currentStep : Subject<number> = new Subject<number>();
somestepChangeMethod(){
this.currentStep.next(<set step here to depending on your logic>);
}
component
// use this in template
currentStep: number;
constructor(stateService : stateServiceClass){
stateService.currentStep.subscribe(val => {
this.currentStep = val;
});
}
Hope this helps!!
It is probably not a good idea to expose your subject inside of your state service. Something like this would be better.
StateService
private currentStep: Subject<number> = new Subject<number>();
changeStep(value: number) {
this.currentStep.next(value);
}
get theCurrentStep(): Observable<number> {
this.currentStep.asObservable();
}
Component
currentStep: number;
constructor(private stateService: StateService) {
this.currentStep = this.stateService.theCurrentStep;
}
Template
[disabled]="(currentStep | async) == 1" // Not sure if this part would work
Let's say I have the following method in a service:
private void deleteItems(List<Item> itemsToDelete) {
def sql = new Sql(dataSource)
itemsToDelete?.each { Item item ->
sql.execute("DELETE FROM owner_item WHERE item_id = ${item.id}")
item.delete(flush: true, failOnError: true)
flushDatabaseSession();
}
}
How do I create a test for this method in the ItemServiceSpec? When I try it, I get either a DataSource "Must specify a non-null Connection" error or a nullPointerException on sql.
This is my existing test.
#TestFor(ItemService)
#Mock([Item])
#Build([Item])
class SubjectServiceSpec extends Specification {
...
def "delete items"() {
given:
Item item1 = Item.build().save(flush: true)
Item item2 = Item.build().save(flush: true)
Item.count() == 2
DataSource mockDataSource = Mock()
service.dataSource = mockDataSource
1 * deleteItems
when:
service.deleteItems([item1, item2])
then:
Item.count() == 0
}
}
What you are trying to do here, is to mock a dependency (DataSource) of a dependency (Sql). This normally leads to a situation, where you a not 100% aware of how the Sql interacts with the DataSource Object. If Sql changes private interaction with the Datasource in a Version Update, you have to deal with the situation.
Instead of mocking a dependency of a dependency you should the Sql Class directly. For this, the sql has to be some kind of explicit dependency that you can receive via DI or a method parameter. In this case you can just mock the execute call like so (choosen the way of a Expando-Mock, but you could also use Map or the Mock Stuff from Spock):
given:
def sqlMock = new Expando()
sqlMock.execute = { return 'what ever you want or nothing, because you mock a delete operation' }
service.sql = sqlMock
when:
service.deleteItems([item1, item2])
then:
assertItemsAreDeletedAndTheOwnerAsWell()
Thinking about the whole testcase, there a two major problems in my opinion.
The first one is, when you ask yourself what kind of certainty do you really get here by mocking out the whole sql stuff. In this case, the only thing that you are doing here is to interact with the db. When you mock this thing out, then there is nothing anymore that you could test. There is not many conditional stuff or anything that should be backed up by a unit test. Due to this, I would suggest to write only integration spec for this test-case where you have something like a H2DB for testing purposes inplace.
The second thing is, that you actually don't need the Sql Manipulation at all. You can configure GORM and Hibernate in a way do a automatic and transparent deletion of the owner of the item, if the item is deleted. For this, look at the docs (especially the cascade part) from GORM or directly in the Hibernate docs.
To sum it up: use cascade: 'delete' together with a proper integration test and you have a high amount of certainty and less boilerplate code.
As part of a Grails project, I'm currently extending my test suite to stuff that need a complete environment to run, like for instance custom HQL and criteria queries, session related issues, etc. I'm using integration tests.
We already have a fairly large number of unit tests (> 500), where we used to mock methods that don't have a Grails mocked counterpart by default (like withCriteria, withNewSession, etc.) using helper functions like this one:
static mockCriteria(Class clazz, returnValue)
{
def myCriteria = new Expando()
myCriteria.list = {Closure cls -> returnValue}
myCriteria.get = {Closure cls -> returnValue}
clazz.metaClass.static.createCriteria = { -> myCriteria }
}
Code could then be tested like this:
mockCriteria(MyClass, [result])
assert myClass.createCriteria.list({ ... }) == [result]
Until now I always managed to meet my needs using this approach.
Now I want to add integration tests that actually check how methods that use criterias and HQL queries behave, in order to facilitate refactoring these queries. I'm stuck by the fact that createCriteria is replaced in my tests and don't recover their initial code after the unit tests phase, preventing me from testing them in the integration phase.
How do you address the problem of mocking criteria (or HQL queries, etc.), while allowing to get the original behavior back in integration tests?
EDIT: Unfortunately, upgrading to Grails 2 is not an option.
I rather mock not criterias, but domain methods that use them.
Plus, Grails 2.0 promise to have criteria support in unit tests - not HQL, though.
Try this:
void setUp() {
super.setUp()
registerMetaClass MyClass
}
Under the Hood
def a = [1, 2, 3]
def oldMetaClass = ArrayList.metaClass
ArrayList.metaClass.find = { Closure cls -> 5 }
assert 5 == a.find{ it == 1}
ArrayList.metaClass = oldMetaClass
assert 1 == a.find{ it == 1 }
And for mockCriteria take a look
https://github.com/fabiooshiro/plastic-criteria
(works with 1.3.7)